From aae688fc92d20b3bc0ab13faeabea51bf4be42de Mon Sep 17 00:00:00 2001 From: Kershaw Chang Date: Wed, 3 May 2017 19:39:00 +0200 Subject: [PATCH 001/131] Bug 1360549 - Give high priority to the message of PNecko constructor, r=mayhemer Since NotifyCurrentTopLevelOuterContentWindowId message has high priority and could jump ahead of PNecko constructor message, we have to also give high priority to PNecko. --- dom/ipc/PContent.ipdl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index 048832e1b6914..f76827e3efcac 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -734,7 +734,9 @@ parent: async PHeapSnapshotTempFileHelper(); - async PNecko(); + // Giving high priority to prevent other messages sending before this one. + // See bug 1360549 for details. + prio(high) async PNecko(); async PPrinting(); From 4a1d038f8b134e8c09b40aaa56a75edf55c5fabb Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Thu, 4 May 2017 01:25:10 -0500 Subject: [PATCH 002/131] Bug 1361642. Return PENDING from FrameAnimator::GetCompositedFrame is a decode is pending so that we don't start another decode. r=aosmond --- image/FrameAnimator.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/image/FrameAnimator.cpp b/image/FrameAnimator.cpp index 4602782af9906..dbd8515b1b5dd 100644 --- a/image/FrameAnimator.cpp +++ b/image/FrameAnimator.cpp @@ -435,11 +435,20 @@ FrameAnimator::RequestRefresh(AnimationState& aState, LookupResult FrameAnimator::GetCompositedFrame(AnimationState& aState) { + LookupResult result = + SurfaceCache::Lookup(ImageKey(mImage), + RasterSurfaceKey(mSize, + DefaultSurfaceFlags(), + PlaybackType::eAnimated)); + if (aState.mCompositedFrameInvalid) { MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable()); MOZ_ASSERT(aState.GetHasBeenDecoded()); MOZ_ASSERT(!aState.GetIsCurrentlyDecoded()); - return LookupResult(MatchType::NOT_FOUND); + if (result.Type() == MatchType::NOT_FOUND) { + return result; + } + return LookupResult(MatchType::PENDING); } // If we have a composited version of this frame, return that. @@ -451,11 +460,6 @@ FrameAnimator::GetCompositedFrame(AnimationState& aState) // Otherwise return the raw frame. DoBlend is required to ensure that we only // hit this case if the frame is not paletted and doesn't require compositing. - LookupResult result = - SurfaceCache::Lookup(ImageKey(mImage), - RasterSurfaceKey(mSize, - DefaultSurfaceFlags(), - PlaybackType::eAnimated)); if (!result) { return result; } @@ -463,7 +467,10 @@ FrameAnimator::GetCompositedFrame(AnimationState& aState) // Seek to the appropriate frame. If seeking fails, it means that we couldn't // get the frame we're looking for; treat this as if the lookup failed. if (NS_FAILED(result.Surface().Seek(aState.mCurrentAnimationFrameIndex))) { - return LookupResult(MatchType::NOT_FOUND); + if (result.Type() == MatchType::NOT_FOUND) { + return result; + } + return LookupResult(MatchType::PENDING); } MOZ_ASSERT(!result.Surface()->GetIsPaletted(), From 87d826fcac6f7a1622eadd45e2ef634434cc2da9 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 08:36:35 +0200 Subject: [PATCH 003/131] Bug 1361654 - Initializing all the member variables in SlicedInputStream, r=qdot --- xpcom/io/SlicedInputStream.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xpcom/io/SlicedInputStream.cpp b/xpcom/io/SlicedInputStream.cpp index 981e6f5a6b4a2..a61ac92779876 100644 --- a/xpcom/io/SlicedInputStream.cpp +++ b/xpcom/io/SlicedInputStream.cpp @@ -38,6 +38,8 @@ SlicedInputStream::SlicedInputStream(nsIInputStream* aInputStream, , mLength(aLength) , mCurPos(0) , mClosed(false) + , mAsyncWaitFlags(0) + , mAsyncWaitRequestedCount(0) { MOZ_ASSERT(aInputStream); SetSourceStream(aInputStream); @@ -52,6 +54,8 @@ SlicedInputStream::SlicedInputStream() , mLength(0) , mCurPos(0) , mClosed(false) + , mAsyncWaitFlags(0) + , mAsyncWaitRequestedCount(0) {} SlicedInputStream::~SlicedInputStream() From 6841da82b56dd9972c903378703e8abe3d93f4bf Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 08:37:54 +0200 Subject: [PATCH 004/131] Bug 1360992 - RecvStreamReady() should be protected by mutex as any other method in IPCBlobInputStreamChild, r=qdot --- dom/file/ipc/IPCBlobInputStreamChild.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/dom/file/ipc/IPCBlobInputStreamChild.cpp b/dom/file/ipc/IPCBlobInputStreamChild.cpp index 228621a952630..6e3ff4fa58c4f 100644 --- a/dom/file/ipc/IPCBlobInputStreamChild.cpp +++ b/dom/file/ipc/IPCBlobInputStreamChild.cpp @@ -167,6 +167,7 @@ IPCBlobInputStreamChild::StreamNeeded(IPCBlobInputStream* aStream, mozilla::ipc::IPCResult IPCBlobInputStreamChild::RecvStreamReady(const OptionalIPCStream& aStream) { + MutexAutoLock lock(mMutex); MOZ_ASSERT(!mPendingOperations.IsEmpty()); nsCOMPtr stream = DeserializeIPCStream(aStream); From 9cb475d6539fa52e86079c7ab571b4621cec0ec1 Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Tue, 2 May 2017 14:44:38 +0200 Subject: [PATCH 005/131] Bug 1338217 - Share code between Wasm instances. r=luke --- js/src/vm/MemoryMetrics.cpp | 4 + js/src/vm/MutexIDs.h | 4 +- js/src/wasm/WasmCode.cpp | 308 +++++++++++++++++++++++++++------- js/src/wasm/WasmCode.h | 91 ++++++---- js/src/wasm/WasmDebug.cpp | 77 +++++---- js/src/wasm/WasmDebug.h | 26 ++- js/src/wasm/WasmGenerator.cpp | 38 ++--- js/src/wasm/WasmInstance.cpp | 4 +- js/src/wasm/WasmInstance.h | 5 + js/src/wasm/WasmJS.cpp | 1 + js/src/wasm/WasmModule.cpp | 121 +++++++------ js/src/wasm/WasmModule.h | 43 +++-- js/src/wasm/WasmTypes.h | 1 + 13 files changed, 504 insertions(+), 219 deletions(-) diff --git a/js/src/vm/MemoryMetrics.cpp b/js/src/vm/MemoryMetrics.cpp index c32aa11022c6c..3cb129465170d 100644 --- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -272,6 +272,7 @@ struct StatsClosure SourceSet seenSources; wasm::Metadata::SeenSet wasmSeenMetadata; wasm::ShareableBytes::SeenSet wasmSeenBytes; + wasm::Code::SeenSet wasmSeenCode; wasm::Table::SeenSet wasmSeenTables; bool anonymize; @@ -285,6 +286,7 @@ struct StatsClosure return seenSources.init() && wasmSeenMetadata.init() && wasmSeenBytes.init() && + wasmSeenCode.init() && wasmSeenTables.init(); } }; @@ -475,6 +477,7 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin module.addSizeOfMisc(rtStats->mallocSizeOf_, &closure->wasmSeenMetadata, &closure->wasmSeenBytes, + &closure->wasmSeenCode, &info.objectsNonHeapCodeWasm, &info.objectsMallocHeapMisc); } else if (obj->is()) { @@ -484,6 +487,7 @@ StatsCellCallback(JSRuntime* rt, void* data, void* thing, JS::TraceKind traceKin instance.addSizeOfMisc(rtStats->mallocSizeOf_, &closure->wasmSeenMetadata, &closure->wasmSeenBytes, + &closure->wasmSeenCode, &closure->wasmSeenTables, &info.objectsNonHeapCodeWasm, &info.objectsMallocHeapMisc); diff --git a/js/src/vm/MutexIDs.h b/js/src/vm/MutexIDs.h index 75be7a0a9659c..b7495aa5c70cf 100644 --- a/js/src/vm/MutexIDs.h +++ b/js/src/vm/MutexIDs.h @@ -34,7 +34,6 @@ _(GeckoProfilerStrings, 500) \ _(ProtectedRegionTree, 500) \ _(WasmSigIdSet, 500) \ - _(WasmCodeProfilingLabels, 500) \ _(ShellOffThreadState, 500) \ _(SimulatorCacheLock, 500) \ _(Arm64SimulatorLock, 500) \ @@ -46,6 +45,9 @@ _(IcuTimeZoneStateMutex, 500) \ _(ProcessExecutableRegion, 500) \ \ + /* WasmCodeProfilingLabels > PromiseTaskPtrVector */ \ + _(WasmCodeProfilingLabels, 550) \ + \ _(TraceLoggerGraphState, 600) \ _(VTuneLock, 600) diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index f33fb75c49a13..a935bee5427de 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -64,7 +64,7 @@ RoundupCodeLength(uint32_t codeLength) } static uint8_t* -AllocateCodeSegment(JSContext* cx, uint32_t codeLength) +AllocateCodeBytes(uint32_t codeLength) { codeLength = RoundupCodeLength(codeLength); @@ -83,20 +83,22 @@ AllocateCodeSegment(JSContext* cx, uint32_t codeLength) } } - if (!p) { - ReportOutOfMemory(cx); + if (!p) return nullptr; - } - cx->zone()->updateJitCodeMallocBytes(codeLength); + // We account for the bytes allocated in WasmModuleObject::create, where we + // have the necessary JSContext. wasmCodeAllocations++; return (uint8_t*)p; } static void -FreeCodeSegment(uint8_t* bytes, uint32_t codeLength) +FreeCodeBytes(uint8_t* bytes, uint32_t codeLength) { + MOZ_ASSERT(wasmCodeAllocations > 0); + wasmCodeAllocations--; + codeLength = RoundupCodeLength(codeLength); #ifdef MOZ_VTUNE vtune::UnmarkBytes(bytes, codeLength); @@ -136,6 +138,33 @@ StaticallyLink(const CodeSegment& cs, const LinkData& linkData) return true; } +static void +StaticallyUnlink(uint8_t* base, const LinkData& linkData) +{ + for (LinkData::InternalLink link : linkData.internalLinks) { + uint8_t* patchAt = base + link.patchAtOffset; + void* target = 0; + if (link.isRawPointerPatch()) + *(void**)(patchAt) = target; + else + Assembler::PatchInstructionImmediate(patchAt, PatchedImmPtr(target)); + } + + for (auto imm : MakeEnumeratedRange(SymbolicAddress::Limit)) { + const Uint32Vector& offsets = linkData.symbolicLinks[imm]; + if (offsets.empty()) + continue; + + void* target = SymbolicAddressTarget(imm); + for (uint32_t offset : offsets) { + uint8_t* patchAt = base + offset; + Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt), + PatchedImmPtr((void*)-1), + PatchedImmPtr(target)); + } + } +} + static void SendCodeRangesToProfiler(const CodeSegment& cs, const Bytes& bytecode, const Metadata& metadata) { @@ -185,53 +214,99 @@ SendCodeRangesToProfiler(const CodeSegment& cs, const Bytes& bytecode, const Met } /* static */ UniqueConstCodeSegment -CodeSegment::create(JSContext* cx, - const Bytes& codeBytes, - const SharedBytes& bytecode, +CodeSegment::create(jit::MacroAssembler& masm, + const ShareableBytes& bytecode, const LinkData& linkData, const Metadata& metadata) { - MOZ_ASSERT(codeBytes.length() % gc::SystemPageSize() == 0); - MOZ_ASSERT(linkData.functionCodeLength < codeBytes.length()); + // Round up the code size to page size since this is eventually required by + // the executable-code allocator and for setting memory protection. + uint32_t bytesNeeded = masm.bytesNeeded(); + uint32_t padding = ComputeByteAlignment(bytesNeeded, gc::SystemPageSize()); + uint32_t codeLength = bytesNeeded + padding; + + MOZ_ASSERT(linkData.functionCodeLength < codeLength); + + uint8_t* codeBase = AllocateCodeBytes(codeLength); + if (!codeBase) + return nullptr; + + // We'll flush the icache after static linking, in initialize(). + masm.executableCopy(codeBase, /* flushICache = */ false); + + // Zero the padding. + memset(codeBase + bytesNeeded, 0, padding); + + return create(codeBase, codeLength, bytecode, linkData, metadata); +} + +/* static */ UniqueConstCodeSegment +CodeSegment::create(const Bytes& unlinkedBytes, const ShareableBytes& bytecode, + const LinkData& linkData, const Metadata& metadata) +{ + uint32_t codeLength = unlinkedBytes.length(); + MOZ_ASSERT(codeLength % gc::SystemPageSize() == 0); + + uint8_t* codeBytes = AllocateCodeBytes(codeLength); + if (!codeBytes) + return nullptr; + memcpy(codeBytes, unlinkedBytes.begin(), codeLength); + return create(codeBytes, codeLength, bytecode, linkData, metadata); +} + +/* static */ UniqueConstCodeSegment +CodeSegment::create(uint8_t* codeBase, uint32_t codeLength, + const ShareableBytes& bytecode, + const LinkData& linkData, + const Metadata& metadata) +{ // These should always exist and should never be first in the code segment. MOZ_ASSERT(linkData.interruptOffset != 0); MOZ_ASSERT(linkData.outOfBoundsOffset != 0); MOZ_ASSERT(linkData.unalignedAccessOffset != 0); - uint8_t* codeBase = AllocateCodeSegment(cx, codeBytes.length()); - if (!codeBase) - return nullptr; - - auto cs = cx->make_unique(codeBase, linkData.functionCodeLength, - codeBytes.length(), - codeBase + linkData.interruptOffset, - codeBase + linkData.outOfBoundsOffset, - codeBase + linkData.unalignedAccessOffset); + auto cs = js::MakeUnique(); if (!cs) { - FreeCodeSegment(codeBase, codeBytes.length()); + FreeCodeBytes(codeBase, codeLength); return nullptr; } - { - JitContext jcx(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())); - AutoFlushICache afc("CodeSegment::create"); - AutoFlushICache::setRange(uintptr_t(codeBase), cs->length()); + if (!cs->initialize(codeBase, codeLength, bytecode, linkData, metadata)) + return nullptr; - memcpy(codeBase, codeBytes.begin(), codeBytes.length()); - if (!StaticallyLink(*cs, linkData)) - return nullptr; - } + return UniqueConstCodeSegment(cs.release()); +} + +bool +CodeSegment::initialize(uint8_t* codeBase, uint32_t codeLength, + const ShareableBytes& bytecode, + const LinkData& linkData, + const Metadata& metadata) +{ + MOZ_ASSERT(bytes_ == nullptr); + + bytes_ = codeBase; + // This CodeSegment instance now owns the code bytes, and the CodeSegment's + // destructor will take care of freeing those bytes in the case of error. + functionLength_ = linkData.functionCodeLength; + length_ = codeLength; + interruptCode_ = codeBase + linkData.interruptOffset; + outOfBoundsCode_ = codeBase + linkData.outOfBoundsOffset; + unalignedAccessCode_ = codeBase + linkData.unalignedAccessOffset; + + if (!StaticallyLink(*this, linkData)) + return false; + + ExecutableAllocator::cacheFlush(codeBase, RoundupCodeLength(codeLength)); // Reprotect the whole region to avoid having separate RW and RX mappings. - if (!ExecutableAllocator::makeExecutable(codeBase, RoundupCodeLength(cs->length()))) { - ReportOutOfMemory(cx); - return nullptr; - } + if (!ExecutableAllocator::makeExecutable(codeBase, RoundupCodeLength(codeLength))) + return false; - SendCodeRangesToProfiler(*cs, bytecode->bytes, metadata); + SendCodeRangesToProfiler(*this, bytecode.bytes, metadata); - return cs; + return true; } CodeSegment::~CodeSegment() @@ -239,12 +314,69 @@ CodeSegment::~CodeSegment() if (!bytes_) return; - MOZ_ASSERT(wasmCodeAllocations > 0); - wasmCodeAllocations--; - MOZ_ASSERT(length() > 0); + FreeCodeBytes(bytes_, length()); +} + +UniqueConstBytes +CodeSegment::unlinkedBytesForDebugging(const LinkData& linkData) const +{ + UniqueBytes unlinkedBytes = js::MakeUnique(); + if (!unlinkedBytes) + return nullptr; + if (!unlinkedBytes->append(base(), length())) + return nullptr; + StaticallyUnlink(unlinkedBytes->begin(), linkData); + return UniqueConstBytes(unlinkedBytes.release()); +} + +size_t +CodeSegment::serializedSize() const +{ + return sizeof(uint32_t) + length_; +} + +void +CodeSegment::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const +{ + *data += mallocSizeOf(this); + *code += RoundupCodeLength(length_); +} + +uint8_t* +CodeSegment::serialize(uint8_t* cursor, const LinkData& linkData) const +{ + cursor = WriteScalar(cursor, length_); + uint8_t* base = cursor; + cursor = WriteBytes(cursor, bytes_, length_); + StaticallyUnlink(base, linkData); + return cursor; +} + +const uint8_t* +CodeSegment::deserialize(const uint8_t* cursor, const ShareableBytes& bytecode, + const LinkData& linkData, const Metadata& metadata) +{ + uint32_t length; + cursor = ReadScalar(cursor, &length); + if (!cursor) + return nullptr; + + MOZ_ASSERT(length_ % gc::SystemPageSize() == 0); + uint8_t* bytes = AllocateCodeBytes(length); + if (!bytes) + return nullptr; + + cursor = ReadBytes(cursor, bytes, length); + if (!cursor) { + FreeCodeBytes(bytes, length); + return nullptr; + } - FreeCodeSegment(bytes_, length()); + if (!initialize(bytes, length, bytecode, linkData, metadata)) + return nullptr; + + return cursor; } size_t @@ -491,6 +623,11 @@ Code::Code(UniqueConstCodeSegment segment, MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode); } +Code::Code() + : profilingLabels_(mutexid::WasmCodeProfilingLabels, CacheableCharsVector()) +{ +} + struct CallSiteRetAddrOffset { const CallSiteVector& callSites; @@ -500,25 +637,73 @@ struct CallSiteRetAddrOffset } }; +size_t +Code::serializedSize() const +{ + return metadata().serializedSize() + + segment().serializedSize(); +} + +uint8_t* +Code::serialize(uint8_t* cursor, const LinkData& linkData) const +{ + MOZ_RELEASE_ASSERT(!metadata().debugEnabled); + + cursor = metadata().serialize(cursor); + cursor = segment().serialize(cursor, linkData); + return cursor; +} + +const uint8_t* +Code::deserialize(const uint8_t* cursor, const SharedBytes& bytecode, const LinkData& linkData, + Metadata* maybeMetadata) +{ + MutableMetadata metadata; + if (maybeMetadata) { + metadata = maybeMetadata; + } else { + metadata = js_new(); + if (!metadata) + return nullptr; + } + cursor = metadata->deserialize(cursor); + if (!cursor) + return nullptr; + + UniqueCodeSegment codeSegment = js::MakeUnique(); + if (!codeSegment) + return nullptr; + + cursor = codeSegment->deserialize(cursor, *bytecode, linkData, *metadata); + if (!cursor) + return nullptr; + + segment_ = UniqueConstCodeSegment(codeSegment.release()); + metadata_ = metadata; + maybeBytecode_ = bytecode; + + return cursor; +} + const CallSite* Code::lookupCallSite(void* returnAddress) const { uint32_t target = ((uint8_t*)returnAddress) - segment_->base(); size_t lowerBound = 0; - size_t upperBound = metadata_->callSites.length(); + size_t upperBound = metadata().callSites.length(); size_t match; - if (!BinarySearch(CallSiteRetAddrOffset(metadata_->callSites), lowerBound, upperBound, target, &match)) + if (!BinarySearch(CallSiteRetAddrOffset(metadata().callSites), lowerBound, upperBound, target, &match)) return nullptr; - return &metadata_->callSites[match]; + return &metadata().callSites[match]; } const CodeRange* Code::lookupRange(void* pc) const { CodeRange::OffsetInCode target((uint8_t*)pc - segment_->base()); - return LookupInSorted(metadata_->codeRanges, target); + return LookupInSorted(metadata().codeRanges, target); } struct MemoryAccessOffset @@ -537,20 +722,20 @@ Code::lookupMemoryAccess(void* pc) const uint32_t target = ((uint8_t*)pc) - segment_->base(); size_t lowerBound = 0; - size_t upperBound = metadata_->memoryAccesses.length(); + size_t upperBound = metadata().memoryAccesses.length(); size_t match; - if (!BinarySearch(MemoryAccessOffset(metadata_->memoryAccesses), lowerBound, upperBound, target, &match)) + if (!BinarySearch(MemoryAccessOffset(metadata().memoryAccesses), lowerBound, upperBound, target, &match)) return nullptr; - return &metadata_->memoryAccesses[match]; + return &metadata().memoryAccesses[match]; } bool Code::getFuncName(uint32_t funcIndex, UTF8Bytes* name) const { const Bytes* maybeBytecode = maybeBytecode_ ? &maybeBytecode_.get()->bytes : nullptr; - return metadata_->getFuncName(maybeBytecode, funcIndex, name); + return metadata().getFuncName(maybeBytecode, funcIndex, name); } JSAtom* @@ -580,7 +765,7 @@ Code::ensureProfilingLabels(bool profilingEnabled) const if (!labels->empty()) return; - for (const CodeRange& codeRange : metadata_->codeRanges) { + for (const CodeRange& codeRange : metadata().codeRanges) { if (!codeRange.isFunction()) continue; @@ -592,7 +777,7 @@ Code::ensureProfilingLabels(bool profilingEnabled) const if (!getFuncName(codeRange.funcIndex(), &name) || !name.append(" (", 2)) return; - if (const char* filename = metadata_->filename.get()) { + if (const char* filename = metadata().filename.get()) { if (!name.append(filename, strlen(filename))) return; } else { @@ -631,15 +816,24 @@ Code::profilingLabel(uint32_t funcIndex) const } void -Code::addSizeOfMisc(MallocSizeOf mallocSizeOf, - Metadata::SeenSet* seenMetadata, - ShareableBytes::SeenSet* seenBytes, - size_t* code, - size_t* data) const -{ - *code += segment_->length(); +Code::addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf, + Metadata::SeenSet* seenMetadata, + ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, + size_t* code, + size_t* data) const +{ + auto p = seenCode->lookupForAdd(this); + if (p) + return; + bool ok = seenCode->add(p, this); + (void)ok; // oh well + *data += mallocSizeOf(this) + - metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata); + metadata().sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) + + profilingLabels_.lock()->sizeOfExcludingThis(mallocSizeOf); + + segment_->addSizeOfMisc(mallocSizeOf, code, data); if (maybeBytecode_) *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); diff --git a/js/src/wasm/WasmCode.h b/js/src/wasm/WasmCode.h index d04de4eebee9d..db39f7016b770 100644 --- a/js/src/wasm/WasmCode.h +++ b/js/src/wasm/WasmCode.h @@ -53,6 +53,7 @@ typedef RefPtr SharedBytes; // A wasm CodeSegment owns the allocated executable code for a wasm module. class CodeSegment; +typedef UniquePtr UniqueCodeSegment; typedef UniquePtr UniqueConstCodeSegment; class CodeSegment @@ -70,32 +71,40 @@ class CodeSegment uint8_t* outOfBoundsCode_; uint8_t* unalignedAccessCode_; - CodeSegment(uint8_t* bytes, uint32_t functionLength, uint32_t length, uint8_t* interruptCode, - uint8_t* outOfBoundsCode, uint8_t* unalignedAccessCode) - : bytes_(bytes), - functionLength_(functionLength), - length_(length), - interruptCode_(interruptCode), - outOfBoundsCode_(outOfBoundsCode), - unalignedAccessCode_(unalignedAccessCode) - { - } - - protected: - CodeSegment() { PodZero(this); } - template friend struct js::MallocProvider; + // This assumes ownership of the codeBytes, and deletes them in the event of error. + bool initialize(uint8_t* codeBase, uint32_t codeLength, const ShareableBytes& bytecode, + const LinkData& linkData, const Metadata& metadata); + // codeBytes must be executable memory. + // This assumes ownership of the codeBytes, and deletes them in the event of error. + static UniqueConstCodeSegment create(uint8_t* codeBytes, + uint32_t codeLength, + const ShareableBytes& bytecode, + const LinkData& linkData, + const Metadata& metadata); + public: CodeSegment(const CodeSegment&) = delete; - CodeSegment(CodeSegment&&) = delete; void operator=(const CodeSegment&) = delete; - void operator=(CodeSegment&&) = delete; - public: - static UniqueConstCodeSegment create(JSContext* cx, - const Bytes& codeBytes, - const SharedBytes& bytecode, + CodeSegment() + : bytes_(nullptr), + functionLength_(0), + length_(0), + interruptCode_(nullptr), + outOfBoundsCode_(nullptr), + unalignedAccessCode_(nullptr) + {} + + static UniqueConstCodeSegment create(jit::MacroAssembler& masm, + const ShareableBytes& bytecode, const LinkData& linkData, const Metadata& metadata); + + static UniqueConstCodeSegment create(const Bytes& codeBytes, + const ShareableBytes& bytecode, + const LinkData& linkData, + const Metadata& metadata); + ~CodeSegment(); uint8_t* base() const { return bytes_; } @@ -117,6 +126,15 @@ class CodeSegment bool containsCodePC(const void* pc) const { return pc >= base() && pc < (base() + length_); } + + UniqueConstBytes unlinkedBytesForDebugging(const LinkData& linkData) const; + + size_t serializedSize() const; + uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const; + const uint8_t* deserialize(const uint8_t* cursor, const ShareableBytes& bytecode, + const LinkData& linkData, const Metadata& metadata); + + void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* code, size_t* data) const; }; // A FuncExport represents a single function definition inside a wasm Module @@ -372,12 +390,14 @@ typedef RefPtr SharedMetadata; class Code : public ShareableBase { - const UniqueConstCodeSegment segment_; - const SharedMetadata metadata_; - const SharedBytes maybeBytecode_; - const ExclusiveData profilingLabels_; + UniqueConstCodeSegment segment_; + SharedMetadata metadata_; + SharedBytes maybeBytecode_; + ExclusiveData profilingLabels_; public: + Code(); + Code(UniqueConstCodeSegment segment, const Metadata& metadata, const ShareableBytes* maybeBytecode); @@ -405,16 +425,25 @@ class Code : public ShareableBase // about:memory reporting: - void addSizeOfMisc(MallocSizeOf mallocSizeOf, - Metadata::SeenSet* seenMetadata, - ShareableBytes::SeenSet* seenBytes, - size_t* code, - size_t* data) const; - - WASM_DECLARE_SERIALIZABLE(Code); + void addSizeOfMiscIfNotSeen(MallocSizeOf mallocSizeOf, + Metadata::SeenSet* seenMetadata, + ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, + size_t* code, + size_t* data) const; + + // A Code object is serialized as the length and bytes of the machine code + // after statically unlinking it; the Code is then later recreated from the + // machine code and other parts. + + size_t serializedSize() const; + uint8_t* serialize(uint8_t* cursor, const LinkData& linkData) const; + const uint8_t* deserialize(const uint8_t* cursor, const SharedBytes& bytecode, + const LinkData& linkData, Metadata* maybeMetadata); }; typedef RefPtr SharedCode; +typedef RefPtr MutableCode; } // namespace wasm } // namespace js diff --git a/js/src/wasm/WasmDebug.cpp b/js/src/wasm/WasmDebug.cpp index 68351977fc660..0bae93de9419e 100644 --- a/js/src/wasm/WasmDebug.cpp +++ b/js/src/wasm/WasmDebug.cpp @@ -75,15 +75,22 @@ GeneratedSourceMap::searchLineByOffset(JSContext* cx, uint32_t offset, size_t* e return true; } +size_t +GeneratedSourceMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const +{ + size_t size = exprlocs_.sizeOfExcludingThis(mallocSizeOf); + if (sortedByOffsetExprLocIndices_) + size += sortedByOffsetExprLocIndices_->sizeOfIncludingThis(mallocSizeOf); + return size; +} + DebugState::DebugState(SharedCode code, - const Metadata& metadata, const ShareableBytes* maybeBytecode) : code_(Move(code)), - metadata_(&metadata), maybeBytecode_(maybeBytecode), enterAndLeaveFrameTrapsCounter_(0) { - MOZ_ASSERT_IF(metadata_->debugEnabled, maybeBytecode); + MOZ_ASSERT_IF(debugEnabled(), maybeBytecode); } const char enabledMessage[] = @@ -157,7 +164,7 @@ struct LineComparator bool DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector* offsets) { - if (!metadata_->debugEnabled) + if (!debugEnabled()) return true; if (!ensureSourceMap(cx)) @@ -190,7 +197,7 @@ bool DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column) { *found = false; - if (!metadata_->debugEnabled) + if (!debugEnabled()) return true; if (!ensureSourceMap(cx)) @@ -214,7 +221,7 @@ bool DebugState::totalSourceLines(JSContext* cx, uint32_t* count) { *count = 0; - if (!metadata_->debugEnabled) + if (!debugEnabled()) return true; if (!ensureSourceMap(cx)) @@ -234,8 +241,8 @@ DebugState::stepModeEnabled(uint32_t funcIndex) const bool DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex) { - MOZ_ASSERT(metadata_->debugEnabled); - const CodeRange& codeRange = metadata_->codeRanges[metadata_->debugFuncToCodeRange[funcIndex]]; + MOZ_ASSERT(debugEnabled()); + const CodeRange& codeRange = codeRanges()[debugFuncToCodeRange(funcIndex)]; MOZ_ASSERT(codeRange.isFunction()); if (!stepModeCounters_.initialized() && !stepModeCounters_.init()) { @@ -258,7 +265,7 @@ DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex) codeRange.end() - codeRange.begin()); AutoFlushICache afc("Code::incrementStepModeCount"); - for (const CallSite& callSite : metadata_->callSites) { + for (const CallSite& callSite : callSites()) { if (callSite.kind() != CallSite::Breakpoint) continue; uint32_t offset = callSite.returnAddressOffset(); @@ -271,8 +278,8 @@ DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex) bool DebugState::decrementStepModeCount(JSContext* cx, uint32_t funcIndex) { - MOZ_ASSERT(metadata_->debugEnabled); - const CodeRange& codeRange = metadata_->codeRanges[metadata_->debugFuncToCodeRange[funcIndex]]; + MOZ_ASSERT(debugEnabled()); + const CodeRange& codeRange = codeRanges()[debugFuncToCodeRange(funcIndex)]; MOZ_ASSERT(codeRange.isFunction()); MOZ_ASSERT(stepModeCounters_.initialized() && !stepModeCounters_.empty()); @@ -287,7 +294,7 @@ DebugState::decrementStepModeCount(JSContext* cx, uint32_t funcIndex) codeRange.end() - codeRange.begin()); AutoFlushICache afc("Code::decrementStepModeCount"); - for (const CallSite& callSite : metadata_->callSites) { + for (const CallSite& callSite : callSites()) { if (callSite.kind() != CallSite::Breakpoint) continue; uint32_t offset = callSite.returnAddressOffset(); @@ -312,16 +319,16 @@ SlowCallSiteSearchByOffset(const Metadata& metadata, uint32_t offset) bool DebugState::hasBreakpointTrapAtOffset(uint32_t offset) { - if (!metadata_->debugEnabled) + if (!debugEnabled()) return false; - return SlowCallSiteSearchByOffset(*metadata_, offset); + return SlowCallSiteSearchByOffset(metadata(), offset); } void DebugState::toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled) { - MOZ_ASSERT(metadata_->debugEnabled); - const CallSite* callSite = SlowCallSiteSearchByOffset(*metadata_, offset); + MOZ_ASSERT(debugEnabled()); + const CallSite* callSite = SlowCallSiteSearchByOffset(metadata(), offset); if (!callSite) return; size_t debugTrapOffset = callSite->returnAddressOffset(); @@ -413,7 +420,7 @@ DebugState::toggleDebugTrap(uint32_t offset, bool enabled) { MOZ_ASSERT(offset); uint8_t* trap = code_->segment().base() + offset; - const Uint32Vector& farJumpOffsets = metadata_->debugTrapFarJumpOffsets; + const Uint32Vector& farJumpOffsets = metadata().debugTrapFarJumpOffsets; if (enabled) { MOZ_ASSERT(farJumpOffsets.length() > 0); size_t i = 0; @@ -432,7 +439,7 @@ DebugState::toggleDebugTrap(uint32_t offset, bool enabled) void DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled) { - MOZ_ASSERT(metadata_->debugEnabled); + MOZ_ASSERT(debugEnabled()); MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0); bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0; @@ -447,7 +454,7 @@ DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled) AutoWritableJitCode awjc(cx->runtime(), code_->segment().base(), code_->segment().length()); AutoFlushICache afc("Code::adjustEnterAndLeaveFrameTrapsState"); AutoFlushICache::setRange(uintptr_t(code_->segment().base()), code_->segment().length()); - for (const CallSite& callSite : metadata_->callSites) { + for (const CallSite& callSite : callSites()) { if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame) continue; toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled); @@ -457,28 +464,28 @@ DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled) bool DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength) { - MOZ_ASSERT(metadata_->debugEnabled); + MOZ_ASSERT(debugEnabled()); - const ValTypeVector& args = metadata_->debugFuncArgTypes[funcIndex]; + const ValTypeVector& args = metadata().debugFuncArgTypes[funcIndex]; *argsLength = args.length(); if (!locals->appendAll(args)) return false; // Decode local var types from wasm binary function body. - const CodeRange& range = metadata_->codeRanges[metadata_->debugFuncToCodeRange[funcIndex]]; + const CodeRange& range = codeRanges()[debugFuncToCodeRange(funcIndex)]; // In wasm, the Code points to the function start via funcLineOrBytecode. - MOZ_ASSERT(!metadata_->isAsmJS() && maybeBytecode_); + MOZ_ASSERT(!metadata().isAsmJS() && maybeBytecode_); size_t offsetInModule = range.funcLineOrBytecode(); Decoder d(maybeBytecode_->begin() + offsetInModule, maybeBytecode_->end(), offsetInModule, /* error = */ nullptr); - return DecodeLocalEntries(d, metadata_->kind, locals); + return DecodeLocalEntries(d, metadata().kind, locals); } ExprType DebugState::debugGetResultType(uint32_t funcIndex) { - MOZ_ASSERT(metadata_->debugEnabled); - return metadata_->debugFuncReturnTypes[funcIndex]; + MOZ_ASSERT(debugEnabled()); + return metadata().debugFuncReturnTypes[funcIndex]; } JSString* @@ -491,7 +498,7 @@ DebugState::debugDisplayURL(JSContext* cx) const js::StringBuffer result(cx); if (!result.append("wasm:")) return nullptr; - if (const char* filename = metadata_->filename.get()) { + if (const char* filename = metadata().filename.get()) { js::StringBuffer filenamePrefix(cx); // EncodeURI returns false due to invalid chars or OOM -- fail only // during OOM. @@ -504,7 +511,7 @@ DebugState::debugDisplayURL(JSContext* cx) const } } - const ModuleHash& hash = metadata_->hash; + const ModuleHash& hash = metadata().hash; for (size_t i = 0; i < sizeof(ModuleHash); i++) { char digit1 = hash[i] / 16, digit2 = hash[i] % 16; if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10))) @@ -513,5 +520,19 @@ DebugState::debugDisplayURL(JSContext* cx) const return nullptr; } return result.finishString(); +} +void +DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf, + Metadata::SeenSet* seenMetadata, + ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, + size_t* code, + size_t* data) const +{ + code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data); + if (maybeSourceMap_) + *data += maybeSourceMap_->sizeOfExcludingThis(mallocSizeOf); + if (maybeBytecode_) + *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); } diff --git a/js/src/wasm/WasmDebug.h b/js/src/wasm/WasmDebug.h index 1691d172be986..8c33b424e7d22 100644 --- a/js/src/wasm/WasmDebug.h +++ b/js/src/wasm/WasmDebug.h @@ -71,6 +71,8 @@ class GeneratedSourceMap void setTotalLines(uint32_t val) { totalLines_ = val; } bool searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex); + + size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; }; typedef UniquePtr UniqueGeneratedSourceMap; @@ -80,11 +82,10 @@ typedef HashMap, SystemAl class DebugState { const SharedCode code_; - const SharedMetadata metadata_; const SharedBytes maybeBytecode_; UniqueGeneratedSourceMap maybeSourceMap_; - // State maintained when debugging is enabled. In this case, the Code is + // State maintained when debugging is enabled. In this case, the Code is // not actually shared, but is referenced uniquely by the instance that is // being debugged. @@ -97,7 +98,6 @@ class DebugState public: DebugState(SharedCode code, - const Metadata& metadata, const ShareableBytes* maybeBytecode); // If the source bytecode was saved when this Code was constructed, this @@ -140,6 +140,26 @@ class DebugState // Debug URL helpers. JSString* debugDisplayURL(JSContext* cx) const; + + // Accessors for commonly used elements of linked structures. + + const Metadata& metadata() const { return code_->metadata(); } + bool debugEnabled() const { return metadata().debugEnabled; } + const CodeRangeVector& codeRanges() const { return metadata().codeRanges; } + const CallSiteVector& callSites() const { return metadata().callSites; } + + uint32_t debugFuncToCodeRange(uint32_t funcIndex) const { + return metadata().debugFuncToCodeRange[funcIndex]; + } + + // about:memory reporting: + + void addSizeOfMisc(MallocSizeOf mallocSizeOf, + Metadata::SeenSet* seenMetadata, + ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, + size_t* code, + size_t* data) const; }; typedef UniquePtr UniqueDebugState; diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index ab2949deb8927..114540e571274 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -1124,25 +1124,6 @@ ModuleGenerator::finish(const ShareableBytes& bytecode) if (!finishCodegen()) return nullptr; - // Round up the code size to page size since this is eventually required by - // the executable-code allocator and for setting memory protection. - uint32_t bytesNeeded = masm_.bytesNeeded(); - uint32_t padding = ComputeByteAlignment(bytesNeeded, gc::SystemPageSize()); - - // Use initLengthUninitialized so there is no round-up allocation nor time - // wasted zeroing memory. - Bytes code; - if (!code.initLengthUninitialized(bytesNeeded + padding)) - return nullptr; - - // We're not copying into executable memory, so don't flush the icache. - // Note: we may be executing on an arbitrary thread without TlsContext set - // so we can't use AutoFlushICache to inhibit. - masm_.executableCopy(code.begin(), /* flushICache = */ false); - - // Zero the padding, since we used resizeUninitialized above. - memset(code.begin() + bytesNeeded, 0, padding); - // Convert the CallSiteAndTargetVector (needed during generation) to a // CallSiteVector (what is stored in the Module). if (!metadata_->callSites.appendAll(masm_.callSites())) @@ -1202,14 +1183,29 @@ ModuleGenerator::finish(const ShareableBytes& bytecode) generateBytecodeHash(bytecode); + UniqueConstCodeSegment codeSegment = CodeSegment::create(masm_, bytecode, linkData_, *metadata_); + if (!codeSegment) + return nullptr; + + UniqueConstBytes maybeDebuggingBytes; + if (metadata_->debugEnabled) { + maybeDebuggingBytes = codeSegment->unlinkedBytesForDebugging(linkData_); + if (!maybeDebuggingBytes) + return nullptr; + } + + SharedCode code = js_new(Move(codeSegment), *metadata_, &bytecode); + if (!code) + return nullptr; + return SharedModule(js_new(Move(assumptions_), - Move(code), + *code, + Move(maybeDebuggingBytes), Move(linkData_), Move(env_->imports), Move(env_->exports), Move(env_->dataSegments), Move(env_->elemSegments), - *metadata_, bytecode)); } diff --git a/js/src/wasm/WasmInstance.cpp b/js/src/wasm/WasmInstance.cpp index eebe765ef3359..97d485f615500 100644 --- a/js/src/wasm/WasmInstance.cpp +++ b/js/src/wasm/WasmInstance.cpp @@ -802,13 +802,15 @@ void Instance::addSizeOfMisc(MallocSizeOf mallocSizeOf, Metadata::SeenSet* seenMetadata, ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, Table::SeenSet* seenTables, size_t* code, size_t* data) const { *data += mallocSizeOf(this) + globals_->sizeOfMisc(mallocSizeOf); + debug_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data); - code_->addSizeOfMisc(mallocSizeOf, seenMetadata, seenBytes, code, data); + code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data); for (const SharedTable& table : tables_) *data += table->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenTables); diff --git a/js/src/wasm/WasmInstance.h b/js/src/wasm/WasmInstance.h index 21176888bcba3..550940f33378e 100644 --- a/js/src/wasm/WasmInstance.h +++ b/js/src/wasm/WasmInstance.h @@ -62,6 +62,10 @@ typedef UniquePtr UniqueGlobalSegment; // instances instantiated from the same Module. However, an Instance has no // direct reference to its source Module which allows a Module to be destroyed // while it still has live Instances. +// +// The instance's code may be shared among multiple instances provided none of +// those instances are being debugged. Instances that are being debugged own +// their code. class Instance { @@ -158,6 +162,7 @@ class Instance void addSizeOfMisc(MallocSizeOf mallocSizeOf, Metadata::SeenSet* seenMetadata, ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, Table::SeenSet* seenTables, size_t* code, size_t* data) const; diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index a1670456d7c0f..a531c222d83af 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -815,6 +815,7 @@ WasmModuleObject::create(JSContext* cx, Module& module, HandleObject proto) obj->initReservedSlot(MODULE_SLOT, PrivateValue(&module)); module.AddRef(); + cx->zone()->updateJitCodeMallocBytes(module.codeLength()); return obj; } diff --git a/js/src/wasm/WasmModule.cpp b/js/src/wasm/WasmModule.cpp index f19a01af05fee..2c1075532b8dc 100644 --- a/js/src/wasm/WasmModule.cpp +++ b/js/src/wasm/WasmModule.cpp @@ -146,18 +146,17 @@ Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) con // The compiled debug code must not be saved, set compiled size to 0, // so Module::assumptionsMatch will return false during assumptions // deserialization. - if (maybeCompiledSize && metadata_->debugEnabled) + if (maybeCompiledSize && metadata().debugEnabled) *maybeCompiledSize = 0; - if (maybeCompiledSize && !metadata_->debugEnabled) { + if (maybeCompiledSize && !metadata().debugEnabled) { *maybeCompiledSize = assumptions_.serializedSize() + - SerializedPodVectorSize(code_) + linkData_.serializedSize() + SerializedVectorSize(imports_) + SerializedVectorSize(exports_) + SerializedPodVectorSize(dataSegments_) + SerializedVectorSize(elemSegments_) + - metadata_->serializedSize(); + code_->serializedSize(); } } @@ -180,22 +179,21 @@ Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize, MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize); } - MOZ_ASSERT_IF(maybeCompiledBegin && metadata_->debugEnabled, maybeCompiledSize == 0); + MOZ_ASSERT_IF(maybeCompiledBegin && metadata().debugEnabled, maybeCompiledSize == 0); - if (maybeCompiledBegin && !metadata_->debugEnabled) { + if (maybeCompiledBegin && !metadata().debugEnabled) { // Assumption must be serialized at the beginning of the compiled bytes so // that compiledAssumptionsMatch can detect a build-id mismatch before any // other decoding occurs. uint8_t* cursor = maybeCompiledBegin; cursor = assumptions_.serialize(cursor); - cursor = SerializePodVector(cursor, code_); cursor = linkData_.serialize(cursor); cursor = SerializeVector(cursor, imports_); cursor = SerializeVector(cursor, exports_); cursor = SerializePodVector(cursor, dataSegments_); cursor = SerializeVector(cursor, elemSegments_); - cursor = metadata_->serialize(cursor); + cursor = code_->serialize(cursor, linkData_); MOZ_RELEASE_ASSERT(cursor == maybeCompiledBegin + maybeCompiledSize); } } @@ -226,11 +224,6 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize, if (!cursor) return nullptr; - Bytes code; - cursor = DeserializePodVector(cursor, &code); - if (!cursor) - return nullptr; - LinkData linkData; cursor = linkData.deserialize(cursor); if (!cursor) @@ -256,29 +249,22 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize, if (!cursor) return nullptr; - MutableMetadata metadata; - if (maybeMetadata) { - metadata = maybeMetadata; - } else { - metadata = js_new(); - if (!metadata) - return nullptr; - } - cursor = metadata->deserialize(cursor); + MutableCode code = js_new(); + cursor = code->deserialize(cursor, bytecode, linkData, maybeMetadata); if (!cursor) return nullptr; MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize); - MOZ_RELEASE_ASSERT(!!maybeMetadata == metadata->isAsmJS()); + MOZ_RELEASE_ASSERT(!!maybeMetadata == code->metadata().isAsmJS()); return js_new(Move(assumptions), - Move(code), + *code, + nullptr, // Serialized code is never debuggable Move(linkData), Move(imports), Move(exports), Move(dataSegments), Move(elemSegments), - *metadata, *bytecode); } @@ -377,19 +363,21 @@ wasm::DeserializeModule(PRFileDesc* bytecodeFile, PRFileDesc* maybeCompiledFile, Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, Metadata::SeenSet* seenMetadata, ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, size_t* code, size_t* data) const { + code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenBytes, seenCode, code, data); *data += mallocSizeOf(this) + assumptions_.sizeOfExcludingThis(mallocSizeOf) + - code_.sizeOfExcludingThis(mallocSizeOf) + linkData_.sizeOfExcludingThis(mallocSizeOf) + SizeOfVectorExcludingThis(imports_, mallocSizeOf) + SizeOfVectorExcludingThis(exports_, mallocSizeOf) + dataSegments_.sizeOfExcludingThis(mallocSizeOf) + SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) + - metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) + bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); + if (unlinkedCodeForDebugging_) + *data += unlinkedCodeForDebugging_->sizeOfExcludingThis(mallocSizeOf); } @@ -398,17 +386,17 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf, // contain offsets in the "code" array and basic information about a code // segment/function body. bool -Module::extractCode(JSContext* cx, MutableHandleValue vp) +Module::extractCode(JSContext* cx, MutableHandleValue vp) const { RootedPlainObject result(cx, NewBuiltinClassInstance(cx)); if (!result) return false; - RootedObject code(cx, JS_NewUint8Array(cx, code_.length())); + RootedObject code(cx, JS_NewUint8Array(cx, code_->segment().length())); if (!code) return false; - memcpy(code->as().viewDataUnshared(), code_.begin(), code_.length()); + memcpy(code->as().viewDataUnshared(), code_->segment().base(), code_->segment().length()); RootedValue value(cx, ObjectValue(*code)); if (!JS_DefineProperty(cx, result, "code", value, JSPROP_ENUMERATE)) @@ -418,7 +406,7 @@ Module::extractCode(JSContext* cx, MutableHandleValue vp) if (!segments) return false; - for (const CodeRange& p : metadata_->codeRanges) { + for (const CodeRange& p : metadata().codeRanges) { RootedObject segment(cx, NewObjectWithGivenProto(cx, nullptr)); if (!segment) return false; @@ -575,12 +563,12 @@ FindImportForFuncImport(const ImportVector& imports, uint32_t funcImportIndex) bool Module::instantiateFunctions(JSContext* cx, Handle funcImports) const { - MOZ_ASSERT(funcImports.length() == metadata_->funcImports.length()); + MOZ_ASSERT(funcImports.length() == metadata().funcImports.length()); if (metadata().isAsmJS()) return true; - for (size_t i = 0; i < metadata_->funcImports.length(); i++) { + for (size_t i = 0; i < metadata().funcImports.length(); i++) { HandleFunction f = funcImports[i]; if (!IsExportedFunction(f) || ExportedFunctionToInstance(f).isAsmJS()) continue; @@ -589,7 +577,7 @@ Module::instantiateFunctions(JSContext* cx, Handle funcImports) Instance& instance = ExportedFunctionToInstance(f); const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex); - if (funcExport.sig() != metadata_->funcImports[i].sig()) { + if (funcExport.sig() != metadata().funcImports[i].sig()) { const Import& import = FindImportForFuncImport(imports_, i); JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMPORT_SIG, import.module.get(), import.field.get()); @@ -630,27 +618,27 @@ CheckLimits(JSContext* cx, uint32_t declaredMin, const Maybe& declared bool Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const { - if (!metadata_->usesMemory()) { + if (!metadata().usesMemory()) { MOZ_ASSERT(!memory); MOZ_ASSERT(dataSegments_.empty()); return true; } - uint32_t declaredMin = metadata_->minMemoryLength; - Maybe declaredMax = metadata_->maxMemoryLength; + uint32_t declaredMin = metadata().minMemoryLength; + Maybe declaredMax = metadata().maxMemoryLength; if (memory) { ArrayBufferObjectMaybeShared& buffer = memory->buffer(); - MOZ_ASSERT_IF(metadata_->isAsmJS(), buffer.isPreparedForAsmJS()); - MOZ_ASSERT_IF(!metadata_->isAsmJS(), buffer.as().isWasm()); + MOZ_ASSERT_IF(metadata().isAsmJS(), buffer.isPreparedForAsmJS()); + MOZ_ASSERT_IF(!metadata().isAsmJS(), buffer.as().isWasm()); if (!CheckLimits(cx, declaredMin, declaredMax, buffer.byteLength(), buffer.wasmMaxSize(), - metadata_->isAsmJS(), "Memory")) { + metadata().isAsmJS(), "Memory")) { return false; } } else { - MOZ_ASSERT(!metadata_->isAsmJS()); - MOZ_ASSERT(metadata_->memoryUsage == MemoryUsage::Unshared); + MOZ_ASSERT(!metadata().isAsmJS()); + MOZ_ASSERT(metadata().memoryUsage == MemoryUsage::Unshared); RootedArrayBufferObjectMaybeShared buffer(cx, ArrayBufferObject::createForWasm(cx, declaredMin, declaredMax)); @@ -672,15 +660,15 @@ Module::instantiateTable(JSContext* cx, MutableHandleWasmTableObject tableObj, SharedTableVector* tables) const { if (tableObj) { - MOZ_ASSERT(!metadata_->isAsmJS()); + MOZ_ASSERT(!metadata().isAsmJS()); - MOZ_ASSERT(metadata_->tables.length() == 1); - const TableDesc& td = metadata_->tables[0]; + MOZ_ASSERT(metadata().tables.length() == 1); + const TableDesc& td = metadata().tables[0]; MOZ_ASSERT(td.external); Table& table = tableObj->table(); if (!CheckLimits(cx, td.limits.initial, td.limits.maximum, table.length(), table.maximum(), - metadata_->isAsmJS(), "Table")) { + metadata().isAsmJS(), "Table")) { return false; } @@ -689,7 +677,7 @@ Module::instantiateTable(JSContext* cx, MutableHandleWasmTableObject tableObj, return false; } } else { - for (const TableDesc& td : metadata_->tables) { + for (const TableDesc& td : metadata().tables) { SharedTable table; if (td.external) { MOZ_ASSERT(!tableObj); @@ -881,16 +869,29 @@ Module::instantiate(JSContext* cx, if (!instantiateTable(cx, &table, &tables)) return false; - // The CodeSegment does not hold on to the bytecode, see comment below. - - auto codeSegment = CodeSegment::create(cx, code_, bytecode_, linkData_, *metadata_); - if (!codeSegment) - return false; - auto globalSegment = GlobalSegment::create(linkData_.globalDataLength); if (!globalSegment) return false; + SharedCode code(code_); + + if (metadata().debugEnabled) { + // The first time through, use the pre-linked code in the module but + // mark it as busy. Subsequently, instantiate the copy of the code + // bytes that we keep around for debugging instead, because the debugger + // may patch the pre-linked code at any time. + if (!codeIsBusy_.compareExchange(false, true)) { + UniqueConstCodeSegment codeSegment = CodeSegment::create(*unlinkedCodeForDebugging_, + *bytecode_, linkData_, + metadata()); + if (!codeSegment) + return false; + code = js_new(Move(codeSegment), metadata(), bytecode_); + if (!code) + return false; + } + } + // To support viewing the source of an instance (Instance::createText), the // instance must hold onto a ref of the bytecode (keeping it alive). This // wastes memory for most users, so we try to only save the source when a @@ -900,21 +901,17 @@ Module::instantiate(JSContext* cx, // for non-developer builds). const ShareableBytes* maybeBytecode = nullptr; - if (cx->compartment()->isDebuggee() || metadata_->debugEnabled || - !metadata_->funcNames.empty()) + if (cx->compartment()->isDebuggee() || metadata().debugEnabled || + !metadata().funcNames.empty()) { maybeBytecode = bytecode_.get(); } - SharedCode code(js_new(Move(codeSegment), *metadata_, maybeBytecode)); - if (!code) - return false; - // The debug object must be present even when debugging is not enabled: It // provides the lazily created source text for the program, even if that // text is a placeholder message when debugging is not enabled. - auto debug = cx->make_unique(code, *metadata_, maybeBytecode); + auto debug = cx->make_unique(code, maybeBytecode); if (!debug) return false; @@ -962,9 +959,9 @@ Module::instantiate(JSContext* cx, // Note that failure may cause instantiation to throw, but the instance may // still be live via edges created by initSegments or the start function. - if (metadata_->startFuncIndex) { + if (metadata().startFuncIndex) { FixedInvokeArgs<0> args(cx); - if (!instance->instance().callExport(cx, *metadata_->startFuncIndex, args)) + if (!instance->instance().callExport(cx, *metadata().startFuncIndex, args)) return false; } diff --git a/js/src/wasm/WasmModule.h b/js/src/wasm/WasmModule.h index 3838b6edcf610..afa17addb49ad 100644 --- a/js/src/wasm/WasmModule.h +++ b/js/src/wasm/WasmModule.h @@ -83,24 +83,32 @@ typedef UniquePtr UniqueConstLinkData; // any number of times such that the serialized bytes can be deserialized later // to produce a new, equivalent Module. // -// Since fully linked-and-instantiated code (represented by CodeSegment) cannot -// be shared between instances, Module stores an unlinked, uninstantiated copy -// of the code (represented by the Bytes) and creates a new CodeSegment each -// time it is instantiated. In the future, Module will store a shareable, -// immutable CodeSegment that can be shared by all its instances. +// Fully linked-and-instantiated code (represented by Code and its owned +// CodeSegment) can be shared between instances, provided none of those +// instances are being debugged. If patchable code is needed then each instance +// must have its own Code. Module eagerly creates a new Code and gives it to the +// first instance; it then instantiates new Code objects from a copy of the +// unlinked code that it keeps around for that purpose. class Module : public JS::WasmModule { const Assumptions assumptions_; - const Bytes code_; + const SharedCode code_; + const UniqueConstBytes unlinkedCodeForDebugging_; const LinkData linkData_; const ImportVector imports_; const ExportVector exports_; const DataSegmentVector dataSegments_; const ElemSegmentVector elemSegments_; - const SharedMetadata metadata_; const SharedBytes bytecode_; + // `codeIsBusy_` is set to false initially and then to true when `code_` is + // already being used for an instance and can't be shared because it may be + // patched by the debugger. Subsequent instances must then create copies + // by linking the `unlinkedCodeForDebugging_`. + + mutable mozilla::Atomic codeIsBusy_; + bool instantiateFunctions(JSContext* cx, Handle funcImports) const; bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const; bool instantiateTable(JSContext* cx, @@ -114,30 +122,34 @@ class Module : public JS::WasmModule public: Module(Assumptions&& assumptions, - Bytes&& code, + const Code& code, + UniqueConstBytes unlinkedCodeForDebugging, LinkData&& linkData, ImportVector&& imports, ExportVector&& exports, DataSegmentVector&& dataSegments, ElemSegmentVector&& elemSegments, - const Metadata& metadata, const ShareableBytes& bytecode) : assumptions_(Move(assumptions)), - code_(Move(code)), + code_(&code), + unlinkedCodeForDebugging_(Move(unlinkedCodeForDebugging)), linkData_(Move(linkData)), imports_(Move(imports)), exports_(Move(exports)), dataSegments_(Move(dataSegments)), elemSegments_(Move(elemSegments)), - metadata_(&metadata), - bytecode_(&bytecode) - {} + bytecode_(&bytecode), + codeIsBusy_(false) + { + MOZ_ASSERT_IF(metadata().debugEnabled, unlinkedCodeForDebugging_); + } ~Module() override { /* Note: can be called on any thread */ } - const Metadata& metadata() const { return *metadata_; } + const Metadata& metadata() const { return code_->metadata(); } const ImportVector& imports() const { return imports_; } const ExportVector& exports() const { return exports_; } const Bytes& bytecode() const { return bytecode_->bytes; } + uint32_t codeLength() const { return code_->segment().length(); } // Instantiate this module with the given imports: @@ -166,11 +178,12 @@ class Module : public JS::WasmModule void addSizeOfMisc(MallocSizeOf mallocSizeOf, Metadata::SeenSet* seenMetadata, ShareableBytes::SeenSet* seenBytes, + Code::SeenSet* seenCode, size_t* code, size_t* data) const; // Generated code analysis support: - bool extractCode(JSContext* cx, MutableHandleValue vp); + bool extractCode(JSContext* cx, MutableHandleValue vp) const; }; typedef RefPtr SharedModule; diff --git a/js/src/wasm/WasmTypes.h b/js/src/wasm/WasmTypes.h index 94ae23f7a056f..df975b66db6ec 100644 --- a/js/src/wasm/WasmTypes.h +++ b/js/src/wasm/WasmTypes.h @@ -89,6 +89,7 @@ using mozilla::Unused; typedef Vector Uint32Vector; typedef Vector Bytes; typedef UniquePtr UniqueBytes; +typedef UniquePtr UniqueConstBytes; typedef Vector UTF8Bytes; typedef int8_t I8x16[16]; From 308ef1c3ffa6837a591c1f3b48cdebe837075c9c Mon Sep 17 00:00:00 2001 From: Lars T Hansen Date: Tue, 2 May 2017 18:05:14 +0200 Subject: [PATCH 006/131] Bug 1338217 - Only preserve bytecode when it's needed, rs=luke --- js/src/wasm/WasmCode.cpp | 6 +++++- js/src/wasm/WasmGenerator.cpp | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/js/src/wasm/WasmCode.cpp b/js/src/wasm/WasmCode.cpp index a935bee5427de..2f4b8ca76c71e 100644 --- a/js/src/wasm/WasmCode.cpp +++ b/js/src/wasm/WasmCode.cpp @@ -678,9 +678,13 @@ Code::deserialize(const uint8_t* cursor, const SharedBytes& bytecode, const Link if (!cursor) return nullptr; + const ShareableBytes* maybeBytecode = nullptr; + if (metadata->debugEnabled || !metadata->funcNames.empty()) + maybeBytecode = bytecode.get(); + segment_ = UniqueConstCodeSegment(codeSegment.release()); metadata_ = metadata; - maybeBytecode_ = bytecode; + maybeBytecode_ = maybeBytecode; return cursor; } diff --git a/js/src/wasm/WasmGenerator.cpp b/js/src/wasm/WasmGenerator.cpp index 114540e571274..c70d92811cb79 100644 --- a/js/src/wasm/WasmGenerator.cpp +++ b/js/src/wasm/WasmGenerator.cpp @@ -1194,7 +1194,11 @@ ModuleGenerator::finish(const ShareableBytes& bytecode) return nullptr; } - SharedCode code = js_new(Move(codeSegment), *metadata_, &bytecode); + const ShareableBytes* maybeBytecode = nullptr; + if (metadata_->debugEnabled || !metadata_->funcNames.empty()) + maybeBytecode = &bytecode; + + SharedCode code = js_new(Move(codeSegment), *metadata_, maybeBytecode); if (!code) return nullptr; From a448b97d906a610be6d3bc68a9c9a3b63c2d06eb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 4 May 2017 00:13:38 -0700 Subject: [PATCH 007/131] Make sure to call ClearTree when creating a temporary APZCTreeManager. (bug 1360478 follow-up, r=kats) --- gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp index 521867c272827..555019d5da05a 100644 --- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp +++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp @@ -128,7 +128,10 @@ CrossProcessCompositorBridgeParent::AllocPAPZCTreeManagerParent(const uint64_t& // to unmap our layers id, and we could get here without a parent compositor. // In this case return an empty APZCTM. if (!state.mParent) { + // Note: we immediately call ClearTree since otherwise the APZCTM will + // retain a reference to itself, through the checkerboard observer. RefPtr temp = new APZCTreeManager(); + temp->ClearTree(); return new APZCTreeManagerParent(aLayersId, temp); } From 40cf4749f93ae70d30d6a110b2e2ad1d7f5be21e Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 4 May 2017 08:17:39 +0100 Subject: [PATCH 008/131] Bug 1360961 - Assert that dying objects are not passed into the JSAPI r=sfink --- js/src/jscntxtinlines.h | 6 ++++-- js/src/jsfun.cpp | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/js/src/jscntxtinlines.h b/js/src/jscntxtinlines.h index 2e1e80e286761..a3cee208ae7f9 100644 --- a/js/src/jscntxtinlines.h +++ b/js/src/jscntxtinlines.h @@ -68,9 +68,11 @@ class CompartmentChecker } void check(JSObject* obj) { - MOZ_ASSERT(JS::ObjectIsNotGray(obj)); - if (obj) + if (obj) { + MOZ_ASSERT(JS::ObjectIsNotGray(obj)); + MOZ_ASSERT(!js::gc::IsAboutToBeFinalizedUnbarriered(&obj)); check(obj->compartment()); + } } template diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 6da5d6aa02d02..aef941ec16fc2 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -732,6 +732,9 @@ js::fun_symbolHasInstance(JSContext* cx, unsigned argc, Value* vp) bool JS::OrdinaryHasInstance(JSContext* cx, HandleObject objArg, HandleValue v, bool* bp) { + AssertHeapIsIdle(); + assertSameCompartment(cx, objArg, v); + RootedObject obj(cx, objArg); /* Step 1. */ From 970e5231c73117cea09128d89c204cb9bd3381e7 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 4 May 2017 08:17:39 +0100 Subject: [PATCH 009/131] Bug 1360961 - Add a barrier to the plugin object wrapper table to handle incrementally finalized JSObjects r=bz --- dom/plugins/base/nsJSNPRuntime.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/dom/plugins/base/nsJSNPRuntime.cpp b/dom/plugins/base/nsJSNPRuntime.cpp index 4f2bdf88ed735..9e3cdd158e749 100644 --- a/dom/plugins/base/nsJSNPRuntime.cpp +++ b/dom/plugins/base/nsJSNPRuntime.cpp @@ -1728,7 +1728,13 @@ NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj) NPObject *npobj = (NPObject *)::JS_GetPrivate(obj); if (npobj) { if (sNPObjWrappers) { - sNPObjWrappers->Remove(npobj); + // If the sNPObjWrappers map contains an entry that refers to this + // wrapper, remove it. + auto entry = + static_cast(sNPObjWrappers->Search(npobj)); + if (entry && entry->mJSObj == obj) { + sNPObjWrappers->Remove(npobj); + } } } @@ -1907,13 +1913,23 @@ nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj) } if (entry->mJSObj) { - // Found a live NPObject wrapper. It may not be in the same compartment - // as cx, so we need to wrap it before returning it. - JS::Rooted obj(cx, entry->mJSObj); - if (!JS_WrapObject(cx, &obj)) { - return nullptr; + // Found a NPObject wrapper. First check it is still alive. + JSObject* obj = entry->mJSObj; + if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) { + // The object is dead (finalization will happen at a later time). By the + // time we leave this function, this entry will either be updated with a + // new wrapper or removed if that fails. Clear it anyway to make sure + // nothing touches the dead object. + entry->mJSObj = nullptr; + } else { + // It may not be in the same compartment as cx, so we need to wrap it + // before returning it. + JS::Rooted obj(cx, entry->mJSObj); + if (!JS_WrapObject(cx, &obj)) { + return nullptr; + } + return obj; } - return obj; } entry->mNPObj = npobj; From 716df7a0a8ac72cf5a0269993e7856ffe218b60a Mon Sep 17 00:00:00 2001 From: Avikalpa Kundu Date: Wed, 3 May 2017 06:38:00 +0200 Subject: [PATCH 010/131] Bug 1344834 - Enable flake8 rule E501: 'line too long (113 > 99 characters)'. r=Dexter --- toolkit/components/telemetry/.flake8 | 1 - .../telemetry/gen-histogram-data.py | 4 +- .../telemetry/gen-histogram-enum.py | 6 ++- .../components/telemetry/histogram_tools.py | 44 ++++++++++++------- toolkit/components/telemetry/parse_events.py | 14 +++--- toolkit/components/telemetry/parse_scalars.py | 6 ++- .../telemetry/shared_telemetry_utils.py | 3 +- 7 files changed, 48 insertions(+), 30 deletions(-) diff --git a/toolkit/components/telemetry/.flake8 b/toolkit/components/telemetry/.flake8 index 4022fc7a2f677..c779495739d8e 100644 --- a/toolkit/components/telemetry/.flake8 +++ b/toolkit/components/telemetry/.flake8 @@ -1,5 +1,4 @@ [flake8] # See http://pep8.readthedocs.io/en/latest/intro.html#configuration -ignore = E501 max-line-length = 99 filename = *.py, +.lint diff --git a/toolkit/components/telemetry/gen-histogram-data.py b/toolkit/components/telemetry/gen-histogram-data.py index 714539f3b9a7d..d27a60d27ea8b 100644 --- a/toolkit/components/telemetry/gen-histogram-data.py +++ b/toolkit/components/telemetry/gen-histogram-data.py @@ -99,8 +99,8 @@ def shared_static_asserts(output, histogram): static_assert(output, "%s < %s" % (low, high), "low >= high for %s" % name) static_assert(output, "%s > 2" % n_buckets, "Not enough values for %s" % name) static_assert(output, "%s >= 1" % low, "Incorrect low value for %s" % name) - static_assert(output, "%s > %s" % (high, n_buckets), - "high must be > number of buckets for %s; you may want an enumerated histogram" % name) + static_assert(output, "%s > %s" % (high, n_buckets), "high must be > number of buckets for %s;" + " you may want an enumerated histogram" % name) def static_asserts_for_linear(output, histogram): diff --git a/toolkit/components/telemetry/gen-histogram-enum.py b/toolkit/components/telemetry/gen-histogram-enum.py index 990b7fbe4e5f4..b647e69f02921 100644 --- a/toolkit/components/telemetry/gen-histogram-enum.py +++ b/toolkit/components/telemetry/gen-histogram-enum.py @@ -85,7 +85,8 @@ def main(output, *filenames): print(" HistogramCount,", file=output) if seen_use_counters: - print(" HistogramUseCounterCount = HistogramLastUseCounter - HistogramFirstUseCounter + 1", file=output) + print(" HistogramUseCounterCount = HistogramLastUseCounter -" + " HistogramFirstUseCounter + 1", file=output) else: print(" HistogramFirstUseCounter = 0,", file=output) print(" HistogramLastUseCounter = 0,", file=output) @@ -106,7 +107,8 @@ def main(output, *filenames): print("\ntemplate struct CategoricalLabelId {};", file=output) for name, _, id in enums: - print("template<> struct CategoricalLabelId<%s> : IntegralConstant {};" % (name, id), file=output) + print("template<> struct CategoricalLabelId<%s> : " + "IntegralConstant {};" % (name, id), file=output) # Footer. print(footer, file=output) diff --git a/toolkit/components/telemetry/histogram_tools.py b/toolkit/components/telemetry/histogram_tools.py index 3258e52da1783..d8aa51e5abf89 100644 --- a/toolkit/components/telemetry/histogram_tools.py +++ b/toolkit/components/telemetry/histogram_tools.py @@ -69,7 +69,8 @@ def exponential_buckets(dmin, dmax, n_buckets): whitelists = None try: - whitelist_path = os.path.join(os.path.abspath(os.path.realpath(os.path.dirname(__file__))), 'histogram-whitelists.json') + whitelist_path = os.path.join(os.path.abspath(os.path.realpath(os.path.dirname(__file__))), + 'histogram-whitelists.json') with open(whitelist_path, 'r') as f: try: whitelists = json.load(f) @@ -79,7 +80,8 @@ def exponential_buckets(dmin, dmax, n_buckets): raise ParserError('Error parsing whitelist: %s' % whitelist_path) except IOError: whitelists = None - print 'Unable to parse whitelist: %s.\nAssuming all histograms are acceptable.' % whitelist_path + print('Unable to parse whitelist: %s.\nAssuming all histograms are acceptable.' % + whitelist_path) class Histogram: @@ -237,7 +239,8 @@ def check_name(self, name): # Avoid C++ identifier conflicts between histogram enums and label enum names. if name.startswith("LABELS_"): - raise ParserError('Error for histogram name "%s": can not start with "LABELS_".' % (name)) + raise ParserError('Error for histogram name "%s": can not start with "LABELS_".' % + (name)) # To make it easier to generate C++ identifiers from this etc., we restrict # the histogram names to a strict pattern. @@ -245,7 +248,8 @@ def check_name(self, name): if self._strict_type_checks: pattern = '^[a-z][a-z0-9_]+[a-z0-9]$' if not re.match(pattern, name, re.IGNORECASE): - raise ParserError('Error for histogram name "%s": name does not conform to "%s"' % (name, pattern)) + raise ParserError('Error for histogram name "%s": name does not conform to "%s"' % + (name, pattern)) def check_expiration(self, name, definition): field = 'expires_in_version' @@ -330,7 +334,8 @@ def check_whitelistable_fields(self, name, definition): if field not in definition and name not in whitelists[field]: raise ParserError('New histogram "%s" must have a "%s" field.' % (name, field)) if field in definition and name in whitelists[field]: - msg = 'Histogram "%s" should be removed from the whitelist for "%s" in histogram-whitelists.json.' + msg = 'Histogram "%s" should be removed from the whitelist for "%s" in ' \ + 'histogram-whitelists.json.' raise ParserError(msg % (name, field)) def check_field_types(self, name, definition): @@ -387,8 +392,8 @@ def nice_type_name(t): if key not in definition: continue if not all(isinstance(x, key_type) for x in definition[key]): - raise ParserError('All values for list "{0}" in histogram "{1}" should be of type {2}.' - .format(key, name, nice_type_name(key_type))) + raise ParserError('All values for list "{0}" in histogram "{1}" should be of type' + ' {2}.'.format(key, name, nice_type_name(key_type))) def check_keys(self, name, definition, allowed_keys): for key in definition.iterkeys(): @@ -401,11 +406,14 @@ def set_bucket_parameters(self, low, high, n_buckets): self._n_buckets = n_buckets if whitelists is not None and self._n_buckets > 100 and type(self._n_buckets) is int: if self._name not in whitelists['n_buckets']: - raise ParserError('New histogram "%s" is not permitted to have more than 100 buckets.\n' - 'Histograms with large numbers of buckets use disproportionately high amounts of resources. ' - 'Contact a Telemetry peer (e.g. in #telemetry) if you think an exception ought to be made:\n' - 'https://wiki.mozilla.org/Modules/Toolkit#Telemetry' - % self._name) + raise ParserError( + 'New histogram "%s" is not permitted to have more than 100 buckets.\n' + 'Histograms with large numbers of buckets use disproportionately high' + ' amounts of resources. Contact a Telemetry peer (e.g. in #telemetry)' + ' if you think an exception ought to be made:\n' + 'https://wiki.mozilla.org/Modules/Toolkit#Telemetry' + % self._name + ) @staticmethod def boolean_flag_bucket_parameters(definition): @@ -425,7 +433,8 @@ def enumerated_bucket_parameters(definition): @staticmethod def categorical_bucket_parameters(definition): # Categorical histograms default to 50 buckets to make working with them easier. - # Otherwise when adding labels later we run into problems with the pipeline not supporting bucket changes. + # Otherwise when adding labels later we run into problems with the pipeline not + # supporting bucket changes. # This can be overridden using the n_values field. n_values = max(len(definition['labels']), definition.get('n_values', 0), @@ -463,7 +472,8 @@ def set_dataset(self, definition): value = definition.get('releaseChannelCollection', 'opt-in') if value not in datasets: - raise ParserError('Unknown value for releaseChannelCollection policy for histogram "%s".' % self._name) + raise ParserError('Unknown value for releaseChannelCollection' + ' policy for histogram "%s".' % self._name) self._dataset = "nsITelemetry::" + datasets[value] @@ -567,12 +577,14 @@ def from_files(filenames): if n_counters != len(use_counter_indices): raise ParserError("Use counter histograms must be defined in a contiguous block.") - # Check that histograms that were removed from Histograms.json etc. are also removed from the whitelists. + # Check that histograms that were removed from Histograms.json etc. + # are also removed from the whitelists. if whitelists is not None: all_whitelist_entries = itertools.chain.from_iterable(whitelists.itervalues()) orphaned = set(all_whitelist_entries) - set(all_histograms.keys()) if len(orphaned) > 0: - msg = 'The following entries are orphaned and should be removed from histogram-whitelists.json:\n%s' + msg = 'The following entries are orphaned and should be removed from ' \ + 'histogram-whitelists.json:\n%s' raise ParserError(msg % (', '.join(sorted(orphaned)))) for (name, definition) in all_histograms.iteritems(): diff --git a/toolkit/components/telemetry/parse_events.py b/toolkit/components/telemetry/parse_events.py index 63dc3b1d41ced..b9d71bf2870fd 100644 --- a/toolkit/components/telemetry/parse_events.py +++ b/toolkit/components/telemetry/parse_events.py @@ -189,7 +189,8 @@ def __init__(self, category, name, definition): record_in_processes = definition.get('record_in_processes') for proc in record_in_processes: if not utils.is_valid_process_name(proc): - raise ParserError(self.identifier + ': Unknown value in record_in_processes: ' + proc) + raise ParserError(self.identifier + ': Unknown value in record_in_processes: ' + + proc) # Check extra_keys. extra_keys = definition.get('extra_keys', {}) @@ -203,18 +204,19 @@ def __init__(self, category, name, definition): # Check expiry. if 'expiry_version' not in definition and 'expiry_date' not in definition: - raise ParserError("%s: event is missing an expiration - either expiry_version or expiry_date is required" % - (self.identifier)) + raise ParserError("%s: event is missing an expiration - either expiry_version or" + " expiry_date is required" % (self.identifier)) expiry_date = definition.get('expiry_date') if expiry_date and isinstance(expiry_date, basestring) and expiry_date != 'never': if not re.match(DATE_PATTERN, expiry_date): - raise ParserError("%s: Event has invalid expiry_date, it should be either 'never' or match this format: %s" % - (self.identifier, DATE_PATTERN)) + raise ParserError("%s: Event has invalid expiry_date, it should be either 'never'" + " or match this format: %s" % (self.identifier, DATE_PATTERN)) # Parse into date. definition['expiry_date'] = datetime.datetime.strptime(expiry_date, '%Y-%m-%d') # Finish setup. - definition['expiry_version'] = utils.add_expiration_postfix(definition.get('expiry_version', 'never')) + definition['expiry_version'] = \ + utils.add_expiration_postfix(definition.get('expiry_version', 'never')) @property def category(self): diff --git a/toolkit/components/telemetry/parse_scalars.py b/toolkit/components/telemetry/parse_scalars.py index 89f9eec552d2b..c364c4923a76c 100644 --- a/toolkit/components/telemetry/parse_scalars.py +++ b/toolkit/components/telemetry/parse_scalars.py @@ -114,7 +114,8 @@ def validate_types(self, definition): # Checks that all the required fields are available. missing_fields = [f for f in REQUIRED_FIELDS.keys() if f not in definition] if len(missing_fields) > 0: - raise ParserError(self._name + ' - missing required fields: ' + ', '.join(missing_fields) + + raise ParserError(self._name + ' - missing required fields: ' + + ', '.join(missing_fields) + '.\nSee: {}#required-fields'.format(BASE_DOC_URL)) # Do we have any unknown field? @@ -125,7 +126,8 @@ def validate_types(self, definition): # Checks the type for all the fields. wrong_type_names = ['{} must be {}'.format(f, ALL_FIELDS[f].__name__) - for f in definition.keys() if not isinstance(definition[f], ALL_FIELDS[f])] + for f in definition.keys() + if not isinstance(definition[f], ALL_FIELDS[f])] if len(wrong_type_names) > 0: raise ParserError(self._name + ' - ' + ', '.join(wrong_type_names) + '.\nSee: {}#required-fields'.format(BASE_DOC_URL)) diff --git a/toolkit/components/telemetry/shared_telemetry_utils.py b/toolkit/components/telemetry/shared_telemetry_utils.py index a41f27dc19805..c991883c1446c 100644 --- a/toolkit/components/telemetry/shared_telemetry_utils.py +++ b/toolkit/components/telemetry/shared_telemetry_utils.py @@ -95,7 +95,8 @@ def toCChar(s): f.write("const char %s[] = {\n" % name) for (string, offset) in entries: if "*/" in string: - raise ValueError("String in string table contains unexpected sequence '*/': %s" % string) + raise ValueError("String in string table contains unexpected sequence '*/': %s" % + string) e = explodeToCharArray(string) if e: From d2aacb6b5ac424d8249c9305009f95e8ccf3bd3d Mon Sep 17 00:00:00 2001 From: Oriol Date: Wed, 3 May 2017 08:09:00 +0200 Subject: [PATCH 011/131] Bug 1349437 - Add "save as" support for JSON Viewer. r=honza Based on https://github.com/mozilla/pdf.js/commit/04599bc4 --- devtools/client/jsonview/converter-child.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/devtools/client/jsonview/converter-child.js b/devtools/client/jsonview/converter-child.js index 77ac88d46bfc6..77c01a332b2b9 100644 --- a/devtools/client/jsonview/converter-child.js +++ b/devtools/client/jsonview/converter-child.js @@ -96,6 +96,10 @@ Converter.prototype = { this.charset = request.QueryInterface(Ci.nsIChannel).contentCharset || "UTF-8"; + // Let "save as" save the original JSON, not the viewer + request.QueryInterface(Ci.nsIWritablePropertyBag); + request.setProperty("contentType", "application/json"); + this.channel = request; this.channel.contentType = "text/html"; this.channel.contentCharset = "UTF-8"; From 76214b2b563d6d44a5490a84c5ce6d5bb2fa24b2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 10:24:13 +0200 Subject: [PATCH 012/131] Bug 1330900 - Implement + + + + + + + + + + diff --git a/dom/html/test/file_script_nomodule.html b/dom/html/test/file_script_nomodule.html new file mode 100644 index 0000000000000..303edb90bb0ce --- /dev/null +++ b/dom/html/test/file_script_nomodule.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + diff --git a/dom/html/test/mochitest.ini b/dom/html/test/mochitest.ini index 78cb54779b120..839c8ebc5b49e 100644 --- a/dom/html/test/mochitest.ini +++ b/dom/html/test/mochitest.ini @@ -608,3 +608,7 @@ skip-if = os == "android" # up/down arrow keys not supported on android [test_bug1310865.html] [test_bug1315146.html] [test_fakepath.html] +[test_script_module.html] +support-files = + file_script_module.html + file_script_nomodule.html diff --git a/dom/html/test/test_script_module.html b/dom/html/test/test_script_module.html new file mode 100644 index 0000000000000..4878bb379fbb0 --- /dev/null +++ b/dom/html/test/test_script_module.html @@ -0,0 +1,56 @@ + + + + Test for HTMLScriptElement with nomodule attribute + + + + + + + + + diff --git a/dom/webidl/HTMLScriptElement.webidl b/dom/webidl/HTMLScriptElement.webidl index 7f5115f1f623a..8bcffe8d74111 100644 --- a/dom/webidl/HTMLScriptElement.webidl +++ b/dom/webidl/HTMLScriptElement.webidl @@ -14,6 +14,8 @@ interface HTMLScriptElement : HTMLElement { attribute DOMString src; [SetterThrows] attribute DOMString type; + [SetterThrows, Pref="dom.moduleScripts.enabled"] + attribute boolean noModule; [SetterThrows] attribute DOMString charset; [SetterThrows] From a4fe120fd2c851a9490bc3f8afb55208a108c644 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 10:24:13 +0200 Subject: [PATCH 013/131] Bug 1330900 - Enable WPT for + + + + + + + + + + From a0190b679bf50164bb68d90426bd3c4f14333513 Mon Sep 17 00:00:00 2001 From: Junior Hsu Date: Fri, 28 Apr 2017 17:14:21 +0800 Subject: [PATCH 040/131] Bug 1360163 - Add test for altData of cache index, r=michal --- netwerk/cache2/AppCacheStorage.cpp | 8 ++++++ netwerk/cache2/CacheStorage.cpp | 2 +- netwerk/cache2/CacheStorage.h | 3 --- netwerk/cache2/OldWrappers.cpp | 8 ++++++ netwerk/cache2/nsICacheStorage.idl | 17 +++++++----- netwerk/test/unit/test_alt-data_simple.js | 33 ++++++++++++++++++++++- 6 files changed, 59 insertions(+), 12 deletions(-) diff --git a/netwerk/cache2/AppCacheStorage.cpp b/netwerk/cache2/AppCacheStorage.cpp index 129a63669e572..7a045175f6263 100644 --- a/netwerk/cache2/AppCacheStorage.cpp +++ b/netwerk/cache2/AppCacheStorage.cpp @@ -171,5 +171,13 @@ NS_IMETHODIMP AppCacheStorage::AsyncVisitStorage(nsICacheStorageVisitor* aVisito return NS_OK; } +NS_IMETHODIMP AppCacheStorage::GetCacheIndexEntryAttrs(nsIURI *aURI, + const nsACString &aIdExtension, + bool *aHasAltData, + uint32_t *aSizeInKB) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + } // namespace net } // namespace mozilla diff --git a/netwerk/cache2/CacheStorage.cpp b/netwerk/cache2/CacheStorage.cpp index 88853b5d7422d..33c8f9e76293f 100644 --- a/netwerk/cache2/CacheStorage.cpp +++ b/netwerk/cache2/CacheStorage.cpp @@ -171,7 +171,7 @@ NS_IMETHODIMP CacheStorage::Exists(nsIURI *aURI, const nsACString & aIdExtension this, asciiSpec, aIdExtension, aResult); } -nsresult +NS_IMETHODIMP CacheStorage::GetCacheIndexEntryAttrs(nsIURI *aURI, const nsACString &aIdExtension, bool *aHasAltData, diff --git a/netwerk/cache2/CacheStorage.h b/netwerk/cache2/CacheStorage.h index b25a4b11a755e..85c5bccdbb315 100644 --- a/netwerk/cache2/CacheStorage.h +++ b/netwerk/cache2/CacheStorage.h @@ -73,9 +73,6 @@ class CacheStorage : public nsICacheStorage bool LookupAppCache() const { return mLookupAppCache; } bool SkipSizeCheck() const { return mSkipSizeCheck; } bool Pinning() const { return mPinning; } - virtual nsresult GetCacheIndexEntryAttrs( - nsIURI *aURI, const nsACString &aIdExtension, - bool *aHasAltData, uint32_t *aSizeInKB) override; }; } // namespace net diff --git a/netwerk/cache2/OldWrappers.cpp b/netwerk/cache2/OldWrappers.cpp index bd083439d7072..543e42bbb756e 100644 --- a/netwerk/cache2/OldWrappers.cpp +++ b/netwerk/cache2/OldWrappers.cpp @@ -1076,6 +1076,14 @@ NS_IMETHODIMP _OldStorage::AsyncVisitStorage(nsICacheStorageVisitor* aVisitor, return NS_OK; } +NS_IMETHODIMP _OldStorage::GetCacheIndexEntryAttrs(nsIURI *aURI, + const nsACString &aIdExtension, + bool *aHasAltData, + uint32_t *aSizeInKB) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + // Internal nsresult _OldStorage::AssembleCacheKey(nsIURI *aURI, diff --git a/netwerk/cache2/nsICacheStorage.idl b/netwerk/cache2/nsICacheStorage.idl index 0a716b1e8284a..8c2a10f454acf 100644 --- a/netwerk/cache2/nsICacheStorage.idl +++ b/netwerk/cache2/nsICacheStorage.idl @@ -113,6 +113,16 @@ interface nsICacheStorage : nsISupports */ boolean exists(in nsIURI aURI, in ACString aIdExtension); + /** + * Synchronously check on existance of alternative data and size of the + * content. When the index data are not up to date or index is still building, + * NS_ERROR_NOT_AVAILABLE is thrown. The same error may throw any storage + * implementation that cannot determine entry state without blocking the caller. + */ + void getCacheIndexEntryAttrs(in nsIURI aURI, + in ACString aIdExtension, + out bool aHasAltData, + out uint32_t aSizeInKB); /** * Asynchronously removes an entry belonging to the URI from the cache. */ @@ -132,11 +142,4 @@ interface nsICacheStorage : nsISupports void asyncVisitStorage(in nsICacheStorageVisitor aVisitor, in boolean aVisitEntries); - %{C++ - virtual nsresult GetCacheIndexEntryAttrs( - nsIURI *aURI, const nsACString &aIdExtension, - bool *aHasAltData, uint32_t *aSizeInKB) { - return NS_ERROR_NOT_IMPLEMENTED; - } - %} }; diff --git a/netwerk/test/unit/test_alt-data_simple.js b/netwerk/test/unit/test_alt-data_simple.js index 6b7bd93a3e5c5..8d5790fc4cb2e 100644 --- a/netwerk/test/unit/test_alt-data_simple.js +++ b/netwerk/test/unit/test_alt-data_simple.js @@ -23,6 +23,12 @@ function make_channel(url, callback, ctx) { return NetUtil.newChannel({uri: url, loadUsingSystemPrincipal: true}); } +function inChildProcess() { + return Cc["@mozilla.org/xre/app-info;1"] + .getService(Ci.nsIXULRuntime) + .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; +} + const responseContent = "response body"; const responseContent2 = "response body 2"; const altContent = "!@#$%^&*()"; @@ -31,6 +37,8 @@ const altContentType = "text/binary"; var servedNotModified = false; var shouldPassRevalidation = true; +var cache_storage = null; + function contentHandler(metadata, response) { response.setHeader("Content-Type", "text/plain"); @@ -52,20 +60,40 @@ function contentHandler(metadata, response) } } +function check_has_alt_data_in_index(aHasAltData) +{ + if (inChildProcess()) { + return; + } + var hasAltData = {}; + cache_storage.getCacheIndexEntryAttrs(createURI(URL), "", hasAltData, {}); + do_check_eq(hasAltData.value, aHasAltData); +} + function run_test() { do_get_profile(); httpServer = new HttpServer(); httpServer.registerPathHandler("/content", contentHandler); httpServer.start(-1); + do_test_pending(); + if (!inChildProcess()) { + cache_storage = getCacheStorage("disk") ; + wait_for_cache_index(asyncOpen); + } else { + asyncOpen(); + } +} + +function asyncOpen() +{ var chan = make_channel(URL); var cc = chan.QueryInterface(Ci.nsICacheInfoChannel); cc.preferAlternativeDataType(altContentType); chan.asyncOpen2(new ChannelListener(readServerContent, null)); - do_test_pending(); } function readServerContent(request, buffer) @@ -74,6 +102,7 @@ function readServerContent(request, buffer) do_check_eq(buffer, responseContent); do_check_eq(cc.alternativeDataType, ""); + check_has_alt_data_in_index(false); do_execute_soon(() => { var os = cc.openAlternativeOutputStream(altContentType); @@ -109,6 +138,7 @@ function readAltContent(request, buffer) do_check_eq(servedNotModified, true); do_check_eq(cc.alternativeDataType, altContentType); do_check_eq(buffer, altContent); + check_has_alt_data_in_index(true); requestAgain(); } @@ -129,6 +159,7 @@ function readEmptyAltContent(request, buffer) // the cache is overwrite and the alt-data is reset do_check_eq(cc.alternativeDataType, ""); do_check_eq(buffer, responseContent2); + check_has_alt_data_in_index(false); httpServer.stop(do_test_finished); } From 480181728f04a9f6f28d2d1ee91baaea3b5ddd0c Mon Sep 17 00:00:00 2001 From: Junior Hsu Date: Tue, 2 May 2017 19:50:00 +0200 Subject: [PATCH 041/131] Bug 1359951 - Ensure that the WebSocket not sending constructor when shutting down, r=nwgh --- netwerk/protocol/websocket/WebSocketChannelChild.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/netwerk/protocol/websocket/WebSocketChannelChild.cpp b/netwerk/protocol/websocket/WebSocketChannelChild.cpp index d96b3ce32e07c..00e8bf6c58291 100644 --- a/netwerk/protocol/websocket/WebSocketChannelChild.cpp +++ b/netwerk/protocol/websocket/WebSocketChannelChild.cpp @@ -528,6 +528,11 @@ WebSocketChannelChild::AsyncOpen(nsIURI *aURI, return NS_ERROR_ILLEGAL_VALUE; } + ContentChild* cc = static_cast(gNeckoChild->Manager()); + if (cc->IsShuttingDown()) { + return NS_ERROR_FAILURE; + } + // Corresponding release in DeallocPWebSocket AddIPDLReference(); From fa9e92eed8b34a72c9e6c3b79e41eb5db19a28f9 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:44:35 +0200 Subject: [PATCH 042/131] Bug 1361443 - nsMultiplexInputStream should implement nsIAsyncInputStream, r=smaug --- xpcom/io/nsMultiplexInputStream.cpp | 216 +++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 3 deletions(-) diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp index de0c10df6cb64..24b6805f51352 100644 --- a/xpcom/io/nsMultiplexInputStream.cpp +++ b/xpcom/io/nsMultiplexInputStream.cpp @@ -12,6 +12,7 @@ #include "mozilla/Attributes.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Mutex.h" +#include "mozilla/SystemGroup.h" #include "base/basictypes.h" @@ -24,6 +25,7 @@ #include "nsIClassInfoImpl.h" #include "nsIIPCSerializableInputStream.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "nsIAsyncInputStream.h" using namespace mozilla; using namespace mozilla::ipc; @@ -38,6 +40,7 @@ class nsMultiplexInputStream final , public nsISeekableStream , public nsIIPCSerializableInputStream , public nsICloneableInputStream + , public nsIAsyncInputStream { public: nsMultiplexInputStream(); @@ -48,6 +51,9 @@ class nsMultiplexInputStream final NS_DECL_NSISEEKABLESTREAM NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM NS_DECL_NSICLONEABLEINPUTSTREAM + NS_DECL_NSIASYNCINPUTSTREAM + + void AsyncWaitCompleted(); private: ~nsMultiplexInputStream() @@ -70,12 +76,14 @@ class nsMultiplexInputStream final bool IsSeekable() const; bool IsIPCSerializable() const; bool IsCloneable() const; + bool IsAsyncInputStream() const; Mutex mLock; // Protects access to all data members. nsTArray> mStreams; uint32_t mCurrentStream; bool mStartedReadingCurrent; nsresult mStatus; + nsCOMPtr mAsyncWaitCallback; }; NS_IMPL_ADDREF(nsMultiplexInputStream) @@ -86,12 +94,14 @@ NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE, NS_INTERFACE_MAP_BEGIN(nsMultiplexInputStream) NS_INTERFACE_MAP_ENTRY(nsIMultiplexInputStream) - NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMultiplexInputStream) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, IsIPCSerializable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, + IsAsyncInputStream()) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream) NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream) NS_INTERFACE_MAP_END @@ -216,6 +226,9 @@ nsMultiplexInputStream::Close() rv = rv2; } } + + mAsyncWaitCallback = nullptr; + return rv; } @@ -308,7 +321,7 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, nsresult rv = NS_OK; ReadSegmentsState state; - state.mThisStream = this; + state.mThisStream = static_cast(this); state.mOffset = 0; state.mWriter = aWriter; state.mClosure = aClosure; @@ -677,6 +690,189 @@ nsMultiplexInputStream::SetEOF() return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsMultiplexInputStream::CloseWithStatus(nsresult aStatus) +{ + return Close(); +} + +// This class is used to inform nsMultiplexInputStream that it's time to execute +// the asyncWait callback. +class AsyncWaitRunnable final : public Runnable +{ + RefPtr mStream; + +public: + explicit AsyncWaitRunnable(nsMultiplexInputStream* aStream) + : Runnable("AsyncWaitRunnable") + , mStream(aStream) + { + MOZ_ASSERT(aStream); + } + + NS_IMETHOD + Run() override + { + mStream->AsyncWaitCompleted(); + return NS_OK; + } +}; + +// This helper class processes an array of nsIAsyncInputStreams, calling +// AsyncWait() for each one of them. When all of them have answered, this helper +// dispatches a AsyncWaitRunnable object. +class AsyncStreamHelper final : public nsIInputStreamCallback +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + static nsresult + Process(nsMultiplexInputStream* aStream, + nsTArray>& aAsyncStreams, + uint32_t aFlags, uint32_t aRequestedCount, + nsIEventTarget* aEventTarget) + { + MOZ_ASSERT(aStream); + MOZ_ASSERT(!aAsyncStreams.IsEmpty()); + MOZ_ASSERT(aEventTarget); + + RefPtr helper = + new AsyncStreamHelper(aStream, aAsyncStreams, aEventTarget); + return helper->Run(aFlags, aRequestedCount); + } + +private: + AsyncStreamHelper(nsMultiplexInputStream* aStream, + nsTArray>& aAsyncStreams, + nsIEventTarget* aEventTarget) + : mMutex("AsyncStreamHelper::mMutex") + , mStream(aStream) + , mEventTarget(aEventTarget) + , mValid(true) + { + mPendingStreams.SwapElements(aAsyncStreams); + } + + ~AsyncStreamHelper() = default; + + nsresult + Run(uint32_t aFlags, uint32_t aRequestedCount) + { + MutexAutoLock lock(mMutex); + + for (uint32_t i = 0; i < mPendingStreams.Length(); ++i) { + nsresult rv = + mPendingStreams[i]->AsyncWait(this, aFlags, aRequestedCount, + mEventTarget); + if (NS_WARN_IF(NS_FAILED(rv))) { + mValid = true; + return rv; + } + } + + return NS_OK; + } + + NS_IMETHOD + OnInputStreamReady(nsIAsyncInputStream* aStream) override + { + MOZ_ASSERT(aStream, "This cannot be one of ours."); + + MutexAutoLock lock(mMutex); + + // We failed during the Run(). + if (!mValid) { + return NS_OK; + } + + MOZ_ASSERT(mPendingStreams.Contains(aStream)); + mPendingStreams.RemoveElement(aStream); + + // The last asyncStream answered. We can inform nsMultiplexInputStream. + if (mPendingStreams.IsEmpty()) { + RefPtr runnable = new AsyncWaitRunnable(mStream); + return mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); + } + + return NS_OK; + } + + Mutex mMutex; + RefPtr mStream; + nsTArray> mPendingStreams; + nsCOMPtr mEventTarget; + bool mValid; +}; + +NS_IMPL_ISUPPORTS(AsyncStreamHelper, nsIInputStreamCallback) + +NS_IMETHODIMP +nsMultiplexInputStream::AsyncWait(nsIInputStreamCallback* aCallback, + uint32_t aFlags, + uint32_t aRequestedCount, + nsIEventTarget* aEventTarget) +{ + // When AsyncWait() is called, it's better to call AsyncWait() to any sub + // stream if they are valid nsIAsyncInputStream instances. In this way, when + // they all call OnInputStreamReady(), we can proceed with the Read(). + + MutexAutoLock lock(mLock); + + if (NS_FAILED(mStatus)) { + return mStatus; + } + + if (mAsyncWaitCallback && aCallback) { + return NS_ERROR_FAILURE; + } + + mAsyncWaitCallback = aCallback; + + if (!mAsyncWaitCallback) { + return NS_OK; + } + + nsTArray> asyncStreams; + for (uint32_t i = mCurrentStream; i < mStreams.Length(); ++i) { + nsCOMPtr asyncStream = + do_QueryInterface(mStreams.SafeElementAt(i, nullptr)); + if (asyncStream) { + asyncStreams.AppendElement(asyncStream); + } + } + + if (!aEventTarget) { + aEventTarget = SystemGroup::EventTargetFor(TaskCategory::Other); + } + + if (asyncStreams.IsEmpty()) { + RefPtr runnable = new AsyncWaitRunnable(this); + return aEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); + } + + return AsyncStreamHelper::Process(this, asyncStreams, aFlags, aRequestedCount, + aEventTarget); +} + +void +nsMultiplexInputStream::AsyncWaitCompleted() +{ + nsCOMPtr callback; + + { + MutexAutoLock lock(mLock); + + // The callback has been nullified in the meantime. + if (!mAsyncWaitCallback) { + return; + } + + mAsyncWaitCallback.swap(callback); + } + + callback->OnInputStreamReady(this); +} + nsresult nsMultiplexInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID, @@ -820,7 +1016,7 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone) return NS_ERROR_FAILURE; } - RefPtr clone = new nsMultiplexInputStream(); + nsCOMPtr clone = new nsMultiplexInputStream(); nsresult rv; uint32_t len = mStreams.Length(); @@ -881,3 +1077,17 @@ nsMultiplexInputStream::IsCloneable() const } return true; } + +bool +nsMultiplexInputStream::IsAsyncInputStream() const +{ + // nsMultiplexInputStream is nsIAsyncInputStream if at least 1 of the + // substream implements that interface. + for (uint32_t i = 0, len = mStreams.Length(); i < len; ++i) { + nsCOMPtr substream = do_QueryInterface(mStreams[i]); + if (substream) { + return true; + } + } + return false; +} From 39ae27561fe65984f885719bda85938ee41e9a25 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:44:53 +0200 Subject: [PATCH 043/131] Bug 1361443 - Tests for remote blobs and multipart inputStreams, r=smaug --- dom/file/ipc/tests/browser_ipcBlob.js | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/dom/file/ipc/tests/browser_ipcBlob.js b/dom/file/ipc/tests/browser_ipcBlob.js index 5ca04b1dbe459..113509a60599a 100644 --- a/dom/file/ipc/tests/browser_ipcBlob.js +++ b/dom/file/ipc/tests/browser_ipcBlob.js @@ -171,3 +171,36 @@ add_task(function* test_CtoPtoC_bc_small() { yield BrowserTestUtils.removeTab(tab1); yield BrowserTestUtils.removeTab(tab2); }); + +// Multipart Blob childA-parent-childB. +add_task(function* test_CtoPtoC_multipart() { + let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); + let browser1 = gBrowser.getBrowserForTab(tab1); + + let blob = yield ContentTask.spawn(browser1, null, function() { + return new Blob(["!"]); + }); + + ok(blob, "CtoPtoC-,ultipart: We have a blob!"); + is(blob.size, "!".length, "CtoPtoC-multipart: The size matches"); + + let newBlob = new Blob(["world", blob]); + + let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); + let browser2 = gBrowser.getBrowserForTab(tab2); + + let status = yield ContentTask.spawn(browser2, newBlob, function(blob) { + return new Promise(resolve => { + let fr = new content.FileReader(); + fr.readAsText(new Blob(["hello ", blob])); + fr.onloadend = function() { + resolve(fr.result == "hello world!"); + } + }); + }); + + ok(status, "CtoPtoC-multipart: Data match!"); + + yield BrowserTestUtils.removeTab(tab1); + yield BrowserTestUtils.removeTab(tab2); +}); From 50018084397c286d71ff71aa0e294418f1f47d45 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:45:34 +0200 Subject: [PATCH 044/131] Bug 1361443 - FileReader should support ReadSegments() not returning the whole size, r=smaug --- dom/file/FileReader.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/dom/file/FileReader.cpp b/dom/file/FileReader.cpp index 37bdbe7fbc5f5..e8e3fa4f66e4d 100644 --- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -280,6 +280,8 @@ FileReader::DoReadData(uint64_t aCount) { MOZ_ASSERT(mAsyncStream); + uint32_t bytesRead = 0; + if (mDataFormat == FILE_AS_BINARY) { //Continuously update our binary string as data comes in uint32_t oldLen = mResult.Length(); @@ -301,14 +303,13 @@ FileReader::DoReadData(uint64_t aCount) NS_ENSURE_SUCCESS(rv, rv); } - uint32_t bytesRead = 0; rv = mBufferedStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - MOZ_ASSERT(bytesRead == aCount, "failed to read data"); + mResult.Truncate(oldLen + bytesRead); } else { CheckedInt size = mDataLen; @@ -322,22 +323,16 @@ FileReader::DoReadData(uint64_t aCount) return NS_ERROR_OUT_OF_MEMORY; } - if (mDataFormat != FILE_AS_ARRAYBUFFER) { - mFileData = (char *) realloc(mFileData, mDataLen + aCount); - NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY); - } - - uint32_t bytesRead = 0; MOZ_DIAGNOSTIC_ASSERT(mFileData); + MOZ_RELEASE_ASSERT((mDataLen + aCount) <= mTotal); + nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - MOZ_ASSERT(bytesRead == aCount, "failed to read data"); } - mDataLen += aCount; + mDataLen += bytesRead; return NS_OK; } @@ -416,8 +411,15 @@ FileReader::ReadFileContent(Blob& aBlob, return; } - if (mDataFormat == FILE_AS_ARRAYBUFFER) { - mFileData = js_pod_malloc(mTotal); + // Binary Format doesn't need a post-processing of the data. Everything is + // written directly into mResult. + if (mDataFormat != FILE_AS_BINARY) { + if (mDataFormat == FILE_AS_ARRAYBUFFER) { + mFileData = js_pod_malloc(mTotal); + } else { + mFileData = (char *) malloc(mTotal); + } + if (!mFileData) { NS_WARNING("Preallocation failed for ReadFileData"); aRv.Throw(NS_ERROR_OUT_OF_MEMORY); From 91af8de1f3ab8a1dda40b11a933e68d31b4fb7c1 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:49:56 +0200 Subject: [PATCH 045/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 1 - SyncRead should read as much as required, r=smaug --- dom/workers/FileReaderSync.cpp | 38 +++++++++++++++++++++++++++------- dom/workers/FileReaderSync.h | 4 ++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 4bf8e5fd6d065..8214f81356a43 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -78,7 +78,7 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, } uint32_t numRead; - aRv = Read(stream, bufferData.get(), blobSize, &numRead); + aRv = SyncRead(stream, bufferData.get(), blobSize, &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -110,7 +110,7 @@ FileReaderSync::ReadAsBinaryString(Blob& aBlob, uint32_t numRead; do { char readBuf[4096]; - aRv = Read(stream, readBuf, sizeof(readBuf), &numRead); + aRv = SyncRead(stream, readBuf, sizeof(readBuf), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -145,7 +145,7 @@ FileReaderSync::ReadAsText(Blob& aBlob, } uint32_t numRead = 0; - aRv = Read(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); + aRv = SyncRead(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -361,8 +361,8 @@ NS_INTERFACE_MAP_END } // anonymous nsresult -FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, - uint32_t* aRead) +FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, + uint32_t aBufferSize, uint32_t* aRead) { MOZ_ASSERT(aStream); MOZ_ASSERT(aBuffer); @@ -370,10 +370,34 @@ FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSiz // Let's try to read, directly. nsresult rv = aStream->Read(aBuffer, aBufferSize, aRead); - if (NS_SUCCEEDED(rv) || rv != NS_BASE_STREAM_WOULD_BLOCK) { + + // Nothing else to read. + if (rv == NS_BASE_STREAM_CLOSED || + (NS_SUCCEEDED(rv) && *aRead == 0)) { + return NS_OK; + } + + // An error. + if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { return rv; } + // All good. + if (NS_SUCCEEDED(rv)) { + // Not enough data, let's read recursively. + if (*aRead != aBufferSize) { + uint32_t byteRead = 0; + rv = SyncRead(aStream, aBuffer + *aRead, aBufferSize - *aRead, &byteRead); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aRead += byteRead; + } + + return NS_OK; + } + // We need to proceed async. nsCOMPtr asyncStream = do_QueryInterface(aStream); if (!asyncStream) { @@ -408,5 +432,5 @@ FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSiz } // Now, we can try to read again. - return Read(aStream, aBuffer, aBufferSize, aRead); + return SyncRead(aStream, aBuffer, aBufferSize, aRead); } diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index c23803161b925..a23877a60abc2 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,8 +32,8 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); - nsresult Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, - uint32_t* aRead); + nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, + uint32_t aBufferSize, uint32_t* aRead); public: static already_AddRefed From 3d94c0fd0b2bb5f0a7e12528fe14c2c33e15fc14 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:50:25 +0200 Subject: [PATCH 046/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 2 - nsIConverterInputStream needs a syncInputStream, r=smaug --- dom/workers/FileReaderSync.cpp | 65 +++++++++++++++++++++++++++++----- dom/workers/FileReaderSync.h | 3 ++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 8214f81356a43..9f9a008666d0b 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -182,16 +182,10 @@ FileReaderSync::ReadAsText(Blob& aBlob, } // Let's recreate the full stream using a: - // multiplexStream(stringStream + original stream) + // multiplexStream(syncStream + original stream) // In theory, we could try to see if the inputStream is a nsISeekableStream, // but this doesn't work correctly for nsPipe3 - See bug 1349570. - nsCOMPtr stringStream; - aRv = NS_NewCStringInputStream(getter_AddRefs(stringStream), sniffBuf); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - nsCOMPtr multiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); if (NS_WARN_IF(!multiplexStream)) { @@ -199,12 +193,24 @@ FileReaderSync::ReadAsText(Blob& aBlob, return; } - aRv = multiplexStream->AppendStream(stringStream); + nsCOMPtr sniffStringStream; + aRv = NS_NewCStringInputStream(getter_AddRefs(sniffStringStream), sniffBuf); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + aRv = multiplexStream->AppendStream(sniffStringStream); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsCOMPtr syncStream; + aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); if (NS_WARN_IF(aRv.Failed())) { return; } - aRv = multiplexStream->AppendStream(stream); + aRv = multiplexStream->AppendStream(syncStream); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -434,3 +440,44 @@ FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, // Now, we can try to read again. return SyncRead(aStream, aBuffer, aBufferSize, aRead); } + +nsresult +FileReaderSync::ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, + nsIInputStream** aSyncStream) +{ + // If the stream is not async, we have nothing to do here. + nsCOMPtr asyncStream = do_QueryInterface(aAsyncStream); + if (!asyncStream) { + nsCOMPtr stream = aAsyncStream; + stream.forget(aSyncStream); + return NS_OK; + } + + uint64_t length; + nsresult rv = aAsyncStream->Available(&length); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoCString buffer; + if (!buffer.SetLength(length, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t read; + rv = SyncRead(aAsyncStream, buffer.BeginWriting(), length, &read); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (read != length) { + return NS_ERROR_FAILURE; + } + + rv = NS_NewCStringInputStream(aSyncStream, buffer); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index a23877a60abc2..6b61f039ee8ab 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,6 +32,9 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); + nsresult ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, + nsIInputStream** aSyncStream); + nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, uint32_t* aRead); From b436524083e4912ecb9233c6130571ee202353c2 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:50:46 +0200 Subject: [PATCH 047/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 2 - tests, r=smaug --- .../tests/test_ipcBlob_fileReaderSync.html | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html index 814e49c71b41b..2a628987d6aaf 100644 --- a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html +++ b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html @@ -13,21 +13,74 @@ onmessage = function(event) { let readerMemoryBlob = new FileReaderSync(); let status = readerMemoryBlob.readAsText(new Blob(['hello world'])) == 'hello world'; + postMessage({ status, message: "FileReaderSync with memory blob still works" }); - let readerIPCBlob = new FileReaderSync(); - postMessage({ blob: event.data, data: readerIPCBlob.readAsText(event.data), status }); + let readerIPCBlob1 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsText', + data: readerIPCBlob1.readAsText(event.data)}); + + let readerIPCBlob2 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsArrayBuffer', + data: readerIPCBlob2.readAsArrayBuffer(event.data)}); + + let readerIPCBlob3 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsDataURL', + data: readerIPCBlob3.readAsDataURL(event.data)}); + + let multipartBlob = new Blob(['wow', event.data]); + + let readerIPCBlobMultipart1 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsText', + data: readerIPCBlobMultipart1.readAsText(multipartBlob)}); + + let readerIPCBlobMultipart2 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsArrayBuffer', + data: readerIPCBlobMultipart2.readAsArrayBuffer(multipartBlob)}); + + let readerIPCBlobMultipart3 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsDataURL', + data: readerIPCBlobMultipart3.readAsDataURL(multipartBlob)}); + + postMessage({ finish: true }); + } +} + +let completed = false; +let pendingTasks = 0; +function maybeFinish() { + if (completed && !pendingTasks) { + SimpleTest.finish(); } } let workerUrl = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); let worker = new Worker(workerUrl); worker.onmessage = event => { - let fr = new FileReader(); - fr.readAsText(event.data.blob); - fr.onload = () => { - is(event.data.data, fr.result, "The file has been read"); - ok(event.data.status, "FileReaderSync with memory blob still works"); - SimpleTest.finish(); + if ("status" in event.data) { + ok(event.data.status, event.data.message); + return; + } + + if ("blob" in event.data) { + let fr = new FileReader(); + fr[event.data.method](event.data.blob); + ++pendingTasks; + fr.onload = () => { + if (event.data.method != 'readAsArrayBuffer') { + is(event.data.data, fr.result, "The file has been read"); + } else { + is(event.data.data.byteLength, fr.result.byteLength, "The file has been read"); + } + --pendingTasks; + maybeFinish(); + } + + return; + } + + if ("finish" in event.data) { + completed = true; + maybeFinish(); } }; From d835830e2c4fe930ddb2d4ee3aff318ad1cb84e8 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:51:11 +0200 Subject: [PATCH 048/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 3 - Base64EncodeInputStream needs a sync inputStream, r=smaug --- dom/workers/FileReaderSync.cpp | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 9f9a008666d0b..5046ec0823028 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -82,7 +82,12 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, if (NS_WARN_IF(aRv.Failed())) { return; } - NS_ASSERTION(numRead == blobSize, "failed to read data"); + + // The file is changed in the meantime? + if (numRead != blobSize) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get()); if (!arrayBuffer) { @@ -150,6 +155,12 @@ FileReaderSync::ReadAsText(Blob& aBlob, return; } + // No data, we don't need to continue. + if (numRead == 0) { + aResult.Truncate(); + return; + } + // The BOM sniffing is baked into the "decode" part of the Encoding // Standard, which the File API references. if (!nsContentUtils::CheckForBOM((const unsigned char*)sniffBuf.BeginReading(), @@ -244,19 +255,30 @@ FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, return; } - uint64_t size = aBlob.GetSize(aRv); - if (NS_WARN_IF(aRv.Failed())){ + nsCOMPtr syncStream; + aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + uint64_t size; + aRv = syncStream->Available(&size); + if (NS_WARN_IF(aRv.Failed())) { return; } - nsCOMPtr bufferedStream; - aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size); + uint64_t blobSize = aBlob.GetSize(aRv); if (NS_WARN_IF(aRv.Failed())){ return; } + // The file is changed in the meantime? + if (blobSize != size) { + return; + } + nsAutoString encodedData; - aRv = Base64EncodeInputStream(bufferedStream, encodedData, size); + aRv = Base64EncodeInputStream(syncStream, encodedData, size); if (NS_WARN_IF(aRv.Failed())){ return; } From 320a91fcb74d44b139bda005664de84ff2c5d86c Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 4 May 2017 13:07:52 +0200 Subject: [PATCH 049/131] Bug 1360390: wasm: Preserve the scratch register in builtin thunks if it's volatile; r=luke Callable prologues/epilogues use the ABINonArgReg0 register as a scratch register, which is non-volatile on ARM. This means it can get clobbered. Instead, just preserve it manually. --- .../jit-test/tests/asm.js/testBug1360390.js | 13 +++++++++++++ js/src/wasm/WasmFrameIterator.cpp | 19 ++++++++++++++++++- js/src/wasm/WasmFrameIterator.h | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/asm.js/testBug1360390.js diff --git a/js/src/jit-test/tests/asm.js/testBug1360390.js b/js/src/jit-test/tests/asm.js/testBug1360390.js new file mode 100644 index 0000000000000..eadd57876cf18 --- /dev/null +++ b/js/src/jit-test/tests/asm.js/testBug1360390.js @@ -0,0 +1,13 @@ +load(libdir + "asm.js"); + +var code = ` + "use asm"; + var ff = foreign.ff; + function f(x) { + x = +x + return ~~x + (ff(3 ^ 9 / 7), 1) & 1; + } + return f; +`; + +assertEq(asmLink(asmCompile("b", "foreign", code), 0, { ff: decodeURIComponent })(Infinity), 1); diff --git a/js/src/wasm/WasmFrameIterator.cpp b/js/src/wasm/WasmFrameIterator.cpp index b495719259309..ad0a223aaaa82 100644 --- a/js/src/wasm/WasmFrameIterator.cpp +++ b/js/src/wasm/WasmFrameIterator.cpp @@ -318,7 +318,6 @@ GenerateCallablePrologue(MacroAssembler& masm, unsigned framePushed, ExitReason #if defined(JS_CODEGEN_ARM) AutoForbidPools afp(&masm, /* number of instructions in scope = */ 8); #endif - *entry = masm.currentOffset(); PushRetAddr(masm, *entry); @@ -333,12 +332,22 @@ GenerateCallablePrologue(MacroAssembler& masm, unsigned framePushed, ExitReason if (!reason.isNone()) { Register scratch = ABINonArgReg0; + + // Native callers expect the native ABI, which assume that non-saved + // registers are preserved. Explicitly preserve the scratch register + // in that case. + if (reason.isNative() && !scratch.volatile_()) + masm.Push(scratch); + masm.loadWasmActivationFromTls(scratch); masm.wasmAssertNonExitInvariants(scratch); Address exitReason(scratch, WasmActivation::offsetOfExitReason()); masm.store32(Imm32(reason.raw()), exitReason); Address exitFP(scratch, WasmActivation::offsetOfExitFP()); masm.storePtr(FramePointer, exitFP); + + if (reason.isNative() && !scratch.volatile_()) + masm.Pop(scratch); } if (framePushed) @@ -354,6 +363,11 @@ GenerateCallableEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason if (!reason.isNone()) { Register scratch = ABINonArgReturnReg0; + + // See comment in GenerateCallablePrologue. + if (reason.isNative() && !scratch.volatile_()) + masm.Push(scratch); + masm.loadWasmActivationFromTls(scratch); Address exitFP(scratch, WasmActivation::offsetOfExitFP()); masm.storePtr(ImmWord(0), exitFP); @@ -369,6 +383,9 @@ GenerateCallableEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason masm.pop(scratch); #endif masm.store32(Imm32(ExitReason::None().raw()), exitReason); + + if (reason.isNative() && !scratch.volatile_()) + masm.Pop(scratch); } // Forbid pools for the same reason as described in GenerateCallablePrologue. diff --git a/js/src/wasm/WasmFrameIterator.h b/js/src/wasm/WasmFrameIterator.h index 16082c2564cee..6e4ff3e8003f3 100644 --- a/js/src/wasm/WasmFrameIterator.h +++ b/js/src/wasm/WasmFrameIterator.h @@ -118,6 +118,7 @@ class ExitReason bool isFixed() const { return (payload_ & 0x1) == 0; } bool isNone() const { return isFixed() && fixed() == Fixed::None; } + bool isNative() const { return !isFixed() || fixed() == Fixed::BuiltinNative; } uint32_t raw() const { return payload_; From 04201d25ab300dce689b70f624781927b82eb372 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 15:42:42 +0200 Subject: [PATCH 050/131] Bug 1345961 - Fixing an assertion about OriginAttribute comparison in PostMessage, r=tjr The OriginAttributes of the 2 windows must match but ignoring the FirstPartDomain isolation. --- dom/base/PostMessageEvent.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dom/base/PostMessageEvent.cpp b/dom/base/PostMessageEvent.cpp index 336b537e0eb3e..3d562859a14e2 100644 --- a/dom/base/PostMessageEvent.cpp +++ b/dom/base/PostMessageEvent.cpp @@ -106,18 +106,17 @@ PostMessageEvent::Run() // now. Long-term, we want HTML5 to address this so that we can // be compliant while being safer. if (!targetPrin->Equals(mProvidedPrincipal)) { + MOZ_DIAGNOSTIC_ASSERT(ChromeUtils::IsOriginAttributesEqualIgnoringFPD(mProvidedPrincipal->OriginAttributesRef(), + targetPrin->OriginAttributesRef()), + "Unexpected postMessage call to a window with mismatched " + "origin attributes"); + nsAutoString providedOrigin, targetOrigin; nsresult rv = nsContentUtils::GetUTFOrigin(targetPrin, targetOrigin); NS_ENSURE_SUCCESS(rv, rv); rv = nsContentUtils::GetUTFOrigin(mProvidedPrincipal, providedOrigin); NS_ENSURE_SUCCESS(rv, rv); - MOZ_DIAGNOSTIC_ASSERT(providedOrigin != targetOrigin || - (mProvidedPrincipal->OriginAttributesRef() == - targetPrin->OriginAttributesRef()), - "Unexpected postMessage call to a window with mismatched " - "origin attributes"); - const char16_t* params[] = { providedOrigin.get(), targetOrigin.get() }; nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, From c57dea5999b341cd2376522b2686e3ec54d6026c Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:04 +0200 Subject: [PATCH 051/131] Backed out changeset 40fcf8d32f3a (bug 1360807) --- dom/workers/FileReaderSync.cpp | 34 ++++++---------------------------- 1 file changed, 6 insertions(+), 28 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 5046ec0823028..9f9a008666d0b 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -82,12 +82,7 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, if (NS_WARN_IF(aRv.Failed())) { return; } - - // The file is changed in the meantime? - if (numRead != blobSize) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } + NS_ASSERTION(numRead == blobSize, "failed to read data"); JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get()); if (!arrayBuffer) { @@ -155,12 +150,6 @@ FileReaderSync::ReadAsText(Blob& aBlob, return; } - // No data, we don't need to continue. - if (numRead == 0) { - aResult.Truncate(); - return; - } - // The BOM sniffing is baked into the "decode" part of the Encoding // Standard, which the File API references. if (!nsContentUtils::CheckForBOM((const unsigned char*)sniffBuf.BeginReading(), @@ -255,30 +244,19 @@ FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, return; } - nsCOMPtr syncStream; - aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - uint64_t size; - aRv = syncStream->Available(&size); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - uint64_t blobSize = aBlob.GetSize(aRv); + uint64_t size = aBlob.GetSize(aRv); if (NS_WARN_IF(aRv.Failed())){ return; } - // The file is changed in the meantime? - if (blobSize != size) { + nsCOMPtr bufferedStream; + aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size); + if (NS_WARN_IF(aRv.Failed())){ return; } nsAutoString encodedData; - aRv = Base64EncodeInputStream(syncStream, encodedData, size); + aRv = Base64EncodeInputStream(bufferedStream, encodedData, size); if (NS_WARN_IF(aRv.Failed())){ return; } From 26c479fce4f5e673d0f1c8cbf14bd32d5ea1c67d Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:10 +0200 Subject: [PATCH 052/131] Backed out changeset d43275b33f00 (bug 1360807) --- .../tests/test_ipcBlob_fileReaderSync.html | 69 +++---------------- 1 file changed, 8 insertions(+), 61 deletions(-) diff --git a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html index 2a628987d6aaf..814e49c71b41b 100644 --- a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html +++ b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html @@ -13,74 +13,21 @@ onmessage = function(event) { let readerMemoryBlob = new FileReaderSync(); let status = readerMemoryBlob.readAsText(new Blob(['hello world'])) == 'hello world'; - postMessage({ status, message: "FileReaderSync with memory blob still works" }); - let readerIPCBlob1 = new FileReaderSync(); - postMessage({ blob: event.data, method: 'readAsText', - data: readerIPCBlob1.readAsText(event.data)}); - - let readerIPCBlob2 = new FileReaderSync(); - postMessage({ blob: event.data, method: 'readAsArrayBuffer', - data: readerIPCBlob2.readAsArrayBuffer(event.data)}); - - let readerIPCBlob3 = new FileReaderSync(); - postMessage({ blob: event.data, method: 'readAsDataURL', - data: readerIPCBlob3.readAsDataURL(event.data)}); - - let multipartBlob = new Blob(['wow', event.data]); - - let readerIPCBlobMultipart1 = new FileReaderSync(); - postMessage({ blob: multipartBlob, method: 'readAsText', - data: readerIPCBlobMultipart1.readAsText(multipartBlob)}); - - let readerIPCBlobMultipart2 = new FileReaderSync(); - postMessage({ blob: multipartBlob, method: 'readAsArrayBuffer', - data: readerIPCBlobMultipart2.readAsArrayBuffer(multipartBlob)}); - - let readerIPCBlobMultipart3 = new FileReaderSync(); - postMessage({ blob: multipartBlob, method: 'readAsDataURL', - data: readerIPCBlobMultipart3.readAsDataURL(multipartBlob)}); - - postMessage({ finish: true }); - } -} - -let completed = false; -let pendingTasks = 0; -function maybeFinish() { - if (completed && !pendingTasks) { - SimpleTest.finish(); + let readerIPCBlob = new FileReaderSync(); + postMessage({ blob: event.data, data: readerIPCBlob.readAsText(event.data), status }); } } let workerUrl = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); let worker = new Worker(workerUrl); worker.onmessage = event => { - if ("status" in event.data) { - ok(event.data.status, event.data.message); - return; - } - - if ("blob" in event.data) { - let fr = new FileReader(); - fr[event.data.method](event.data.blob); - ++pendingTasks; - fr.onload = () => { - if (event.data.method != 'readAsArrayBuffer') { - is(event.data.data, fr.result, "The file has been read"); - } else { - is(event.data.data.byteLength, fr.result.byteLength, "The file has been read"); - } - --pendingTasks; - maybeFinish(); - } - - return; - } - - if ("finish" in event.data) { - completed = true; - maybeFinish(); + let fr = new FileReader(); + fr.readAsText(event.data.blob); + fr.onload = () => { + is(event.data.data, fr.result, "The file has been read"); + ok(event.data.status, "FileReaderSync with memory blob still works"); + SimpleTest.finish(); } }; From 37fb7e6f9d5503c11f6cbee058d0069ff6d0525e Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:14 +0200 Subject: [PATCH 053/131] Backed out changeset 6b389f6ad971 (bug 1360807) --- dom/workers/FileReaderSync.cpp | 65 +++++----------------------------- dom/workers/FileReaderSync.h | 3 -- 2 files changed, 9 insertions(+), 59 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 9f9a008666d0b..8214f81356a43 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -182,35 +182,29 @@ FileReaderSync::ReadAsText(Blob& aBlob, } // Let's recreate the full stream using a: - // multiplexStream(syncStream + original stream) + // multiplexStream(stringStream + original stream) // In theory, we could try to see if the inputStream is a nsISeekableStream, // but this doesn't work correctly for nsPipe3 - See bug 1349570. - nsCOMPtr multiplexStream = - do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); - if (NS_WARN_IF(!multiplexStream)) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - - nsCOMPtr sniffStringStream; - aRv = NS_NewCStringInputStream(getter_AddRefs(sniffStringStream), sniffBuf); + nsCOMPtr stringStream; + aRv = NS_NewCStringInputStream(getter_AddRefs(stringStream), sniffBuf); if (NS_WARN_IF(aRv.Failed())) { return; } - aRv = multiplexStream->AppendStream(sniffStringStream); - if (NS_WARN_IF(aRv.Failed())) { + nsCOMPtr multiplexStream = + do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); + if (NS_WARN_IF(!multiplexStream)) { + aRv.Throw(NS_ERROR_FAILURE); return; } - nsCOMPtr syncStream; - aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); + aRv = multiplexStream->AppendStream(stringStream); if (NS_WARN_IF(aRv.Failed())) { return; } - aRv = multiplexStream->AppendStream(syncStream); + aRv = multiplexStream->AppendStream(stream); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -440,44 +434,3 @@ FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, // Now, we can try to read again. return SyncRead(aStream, aBuffer, aBufferSize, aRead); } - -nsresult -FileReaderSync::ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, - nsIInputStream** aSyncStream) -{ - // If the stream is not async, we have nothing to do here. - nsCOMPtr asyncStream = do_QueryInterface(aAsyncStream); - if (!asyncStream) { - nsCOMPtr stream = aAsyncStream; - stream.forget(aSyncStream); - return NS_OK; - } - - uint64_t length; - nsresult rv = aAsyncStream->Available(&length); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - nsAutoCString buffer; - if (!buffer.SetLength(length, fallible)) { - return NS_ERROR_OUT_OF_MEMORY; - } - - uint32_t read; - rv = SyncRead(aAsyncStream, buffer.BeginWriting(), length, &read); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - if (read != length) { - return NS_ERROR_FAILURE; - } - - rv = NS_NewCStringInputStream(aSyncStream, buffer); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - return NS_OK; -} diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index 6b61f039ee8ab..a23877a60abc2 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,9 +32,6 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); - nsresult ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, - nsIInputStream** aSyncStream); - nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, uint32_t* aRead); From 718462005559690221e8b6a40a1b7b810c5871b5 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:16 +0200 Subject: [PATCH 054/131] Backed out changeset 8275594fe235 (bug 1360807) --- dom/workers/FileReaderSync.cpp | 38 +++++++--------------------------- dom/workers/FileReaderSync.h | 4 ++-- 2 files changed, 9 insertions(+), 33 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 8214f81356a43..4bf8e5fd6d065 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -78,7 +78,7 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, } uint32_t numRead; - aRv = SyncRead(stream, bufferData.get(), blobSize, &numRead); + aRv = Read(stream, bufferData.get(), blobSize, &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -110,7 +110,7 @@ FileReaderSync::ReadAsBinaryString(Blob& aBlob, uint32_t numRead; do { char readBuf[4096]; - aRv = SyncRead(stream, readBuf, sizeof(readBuf), &numRead); + aRv = Read(stream, readBuf, sizeof(readBuf), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -145,7 +145,7 @@ FileReaderSync::ReadAsText(Blob& aBlob, } uint32_t numRead = 0; - aRv = SyncRead(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); + aRv = Read(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -361,8 +361,8 @@ NS_INTERFACE_MAP_END } // anonymous nsresult -FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, - uint32_t aBufferSize, uint32_t* aRead) +FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, + uint32_t* aRead) { MOZ_ASSERT(aStream); MOZ_ASSERT(aBuffer); @@ -370,34 +370,10 @@ FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, // Let's try to read, directly. nsresult rv = aStream->Read(aBuffer, aBufferSize, aRead); - - // Nothing else to read. - if (rv == NS_BASE_STREAM_CLOSED || - (NS_SUCCEEDED(rv) && *aRead == 0)) { - return NS_OK; - } - - // An error. - if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { + if (NS_SUCCEEDED(rv) || rv != NS_BASE_STREAM_WOULD_BLOCK) { return rv; } - // All good. - if (NS_SUCCEEDED(rv)) { - // Not enough data, let's read recursively. - if (*aRead != aBufferSize) { - uint32_t byteRead = 0; - rv = SyncRead(aStream, aBuffer + *aRead, aBufferSize - *aRead, &byteRead); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - - *aRead += byteRead; - } - - return NS_OK; - } - // We need to proceed async. nsCOMPtr asyncStream = do_QueryInterface(aStream); if (!asyncStream) { @@ -432,5 +408,5 @@ FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, } // Now, we can try to read again. - return SyncRead(aStream, aBuffer, aBufferSize, aRead); + return Read(aStream, aBuffer, aBufferSize, aRead); } diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index a23877a60abc2..c23803161b925 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,8 +32,8 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); - nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, - uint32_t aBufferSize, uint32_t* aRead); + nsresult Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, + uint32_t* aRead); public: static already_AddRefed From 10920a15ee61b12a64c0033e0ffe11a0e54afa3b Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:22 +0200 Subject: [PATCH 055/131] Backed out changeset 4370dbfde05c (bug 1361443) --- dom/file/FileReader.cpp | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/dom/file/FileReader.cpp b/dom/file/FileReader.cpp index e8e3fa4f66e4d..37bdbe7fbc5f5 100644 --- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -280,8 +280,6 @@ FileReader::DoReadData(uint64_t aCount) { MOZ_ASSERT(mAsyncStream); - uint32_t bytesRead = 0; - if (mDataFormat == FILE_AS_BINARY) { //Continuously update our binary string as data comes in uint32_t oldLen = mResult.Length(); @@ -303,13 +301,14 @@ FileReader::DoReadData(uint64_t aCount) NS_ENSURE_SUCCESS(rv, rv); } + uint32_t bytesRead = 0; rv = mBufferedStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - mResult.Truncate(oldLen + bytesRead); + MOZ_ASSERT(bytesRead == aCount, "failed to read data"); } else { CheckedInt size = mDataLen; @@ -323,16 +322,22 @@ FileReader::DoReadData(uint64_t aCount) return NS_ERROR_OUT_OF_MEMORY; } - MOZ_DIAGNOSTIC_ASSERT(mFileData); - MOZ_RELEASE_ASSERT((mDataLen + aCount) <= mTotal); + if (mDataFormat != FILE_AS_ARRAYBUFFER) { + mFileData = (char *) realloc(mFileData, mDataLen + aCount); + NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY); + } + uint32_t bytesRead = 0; + MOZ_DIAGNOSTIC_ASSERT(mFileData); nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + + MOZ_ASSERT(bytesRead == aCount, "failed to read data"); } - mDataLen += bytesRead; + mDataLen += aCount; return NS_OK; } @@ -411,15 +416,8 @@ FileReader::ReadFileContent(Blob& aBlob, return; } - // Binary Format doesn't need a post-processing of the data. Everything is - // written directly into mResult. - if (mDataFormat != FILE_AS_BINARY) { - if (mDataFormat == FILE_AS_ARRAYBUFFER) { - mFileData = js_pod_malloc(mTotal); - } else { - mFileData = (char *) malloc(mTotal); - } - + if (mDataFormat == FILE_AS_ARRAYBUFFER) { + mFileData = js_pod_malloc(mTotal); if (!mFileData) { NS_WARNING("Preallocation failed for ReadFileData"); aRv.Throw(NS_ERROR_OUT_OF_MEMORY); From a43ed762484155c35e609cc911cab4437b510af0 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:39:26 +0200 Subject: [PATCH 056/131] Backed out changeset 1ab58ab887c6 (bug 1361443) --- dom/file/ipc/tests/browser_ipcBlob.js | 33 --------------------------- 1 file changed, 33 deletions(-) diff --git a/dom/file/ipc/tests/browser_ipcBlob.js b/dom/file/ipc/tests/browser_ipcBlob.js index 113509a60599a..5ca04b1dbe459 100644 --- a/dom/file/ipc/tests/browser_ipcBlob.js +++ b/dom/file/ipc/tests/browser_ipcBlob.js @@ -171,36 +171,3 @@ add_task(function* test_CtoPtoC_bc_small() { yield BrowserTestUtils.removeTab(tab1); yield BrowserTestUtils.removeTab(tab2); }); - -// Multipart Blob childA-parent-childB. -add_task(function* test_CtoPtoC_multipart() { - let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); - let browser1 = gBrowser.getBrowserForTab(tab1); - - let blob = yield ContentTask.spawn(browser1, null, function() { - return new Blob(["!"]); - }); - - ok(blob, "CtoPtoC-,ultipart: We have a blob!"); - is(blob.size, "!".length, "CtoPtoC-multipart: The size matches"); - - let newBlob = new Blob(["world", blob]); - - let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); - let browser2 = gBrowser.getBrowserForTab(tab2); - - let status = yield ContentTask.spawn(browser2, newBlob, function(blob) { - return new Promise(resolve => { - let fr = new content.FileReader(); - fr.readAsText(new Blob(["hello ", blob])); - fr.onloadend = function() { - resolve(fr.result == "hello world!"); - } - }); - }); - - ok(status, "CtoPtoC-multipart: Data match!"); - - yield BrowserTestUtils.removeTab(tab1); - yield BrowserTestUtils.removeTab(tab2); -}); From 3014dcb24f9cd576acb6d66aabf5b843f6003be7 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:40:14 +0200 Subject: [PATCH 057/131] Backed out changeset c0e3f3edf36a (bug 1361443) for crashes in [@ mozilla::Base64EncodeInputStream] and test failures in test_fileReaderSync.xul --- xpcom/io/nsMultiplexInputStream.cpp | 216 +--------------------------- 1 file changed, 3 insertions(+), 213 deletions(-) diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp index 24b6805f51352..de0c10df6cb64 100644 --- a/xpcom/io/nsMultiplexInputStream.cpp +++ b/xpcom/io/nsMultiplexInputStream.cpp @@ -12,7 +12,6 @@ #include "mozilla/Attributes.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Mutex.h" -#include "mozilla/SystemGroup.h" #include "base/basictypes.h" @@ -25,7 +24,6 @@ #include "nsIClassInfoImpl.h" #include "nsIIPCSerializableInputStream.h" #include "mozilla/ipc/InputStreamUtils.h" -#include "nsIAsyncInputStream.h" using namespace mozilla; using namespace mozilla::ipc; @@ -40,7 +38,6 @@ class nsMultiplexInputStream final , public nsISeekableStream , public nsIIPCSerializableInputStream , public nsICloneableInputStream - , public nsIAsyncInputStream { public: nsMultiplexInputStream(); @@ -51,9 +48,6 @@ class nsMultiplexInputStream final NS_DECL_NSISEEKABLESTREAM NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM NS_DECL_NSICLONEABLEINPUTSTREAM - NS_DECL_NSIASYNCINPUTSTREAM - - void AsyncWaitCompleted(); private: ~nsMultiplexInputStream() @@ -76,14 +70,12 @@ class nsMultiplexInputStream final bool IsSeekable() const; bool IsIPCSerializable() const; bool IsCloneable() const; - bool IsAsyncInputStream() const; Mutex mLock; // Protects access to all data members. nsTArray> mStreams; uint32_t mCurrentStream; bool mStartedReadingCurrent; nsresult mStatus; - nsCOMPtr mAsyncWaitCallback; }; NS_IMPL_ADDREF(nsMultiplexInputStream) @@ -94,14 +86,12 @@ NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE, NS_INTERFACE_MAP_BEGIN(nsMultiplexInputStream) NS_INTERFACE_MAP_ENTRY(nsIMultiplexInputStream) - NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMultiplexInputStream) + NS_INTERFACE_MAP_ENTRY(nsIInputStream) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, IsIPCSerializable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable()) - NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, - IsAsyncInputStream()) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream) NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream) NS_INTERFACE_MAP_END @@ -226,9 +216,6 @@ nsMultiplexInputStream::Close() rv = rv2; } } - - mAsyncWaitCallback = nullptr; - return rv; } @@ -321,7 +308,7 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, nsresult rv = NS_OK; ReadSegmentsState state; - state.mThisStream = static_cast(this); + state.mThisStream = this; state.mOffset = 0; state.mWriter = aWriter; state.mClosure = aClosure; @@ -690,189 +677,6 @@ nsMultiplexInputStream::SetEOF() return NS_ERROR_NOT_IMPLEMENTED; } -NS_IMETHODIMP -nsMultiplexInputStream::CloseWithStatus(nsresult aStatus) -{ - return Close(); -} - -// This class is used to inform nsMultiplexInputStream that it's time to execute -// the asyncWait callback. -class AsyncWaitRunnable final : public Runnable -{ - RefPtr mStream; - -public: - explicit AsyncWaitRunnable(nsMultiplexInputStream* aStream) - : Runnable("AsyncWaitRunnable") - , mStream(aStream) - { - MOZ_ASSERT(aStream); - } - - NS_IMETHOD - Run() override - { - mStream->AsyncWaitCompleted(); - return NS_OK; - } -}; - -// This helper class processes an array of nsIAsyncInputStreams, calling -// AsyncWait() for each one of them. When all of them have answered, this helper -// dispatches a AsyncWaitRunnable object. -class AsyncStreamHelper final : public nsIInputStreamCallback -{ -public: - NS_DECL_THREADSAFE_ISUPPORTS - - static nsresult - Process(nsMultiplexInputStream* aStream, - nsTArray>& aAsyncStreams, - uint32_t aFlags, uint32_t aRequestedCount, - nsIEventTarget* aEventTarget) - { - MOZ_ASSERT(aStream); - MOZ_ASSERT(!aAsyncStreams.IsEmpty()); - MOZ_ASSERT(aEventTarget); - - RefPtr helper = - new AsyncStreamHelper(aStream, aAsyncStreams, aEventTarget); - return helper->Run(aFlags, aRequestedCount); - } - -private: - AsyncStreamHelper(nsMultiplexInputStream* aStream, - nsTArray>& aAsyncStreams, - nsIEventTarget* aEventTarget) - : mMutex("AsyncStreamHelper::mMutex") - , mStream(aStream) - , mEventTarget(aEventTarget) - , mValid(true) - { - mPendingStreams.SwapElements(aAsyncStreams); - } - - ~AsyncStreamHelper() = default; - - nsresult - Run(uint32_t aFlags, uint32_t aRequestedCount) - { - MutexAutoLock lock(mMutex); - - for (uint32_t i = 0; i < mPendingStreams.Length(); ++i) { - nsresult rv = - mPendingStreams[i]->AsyncWait(this, aFlags, aRequestedCount, - mEventTarget); - if (NS_WARN_IF(NS_FAILED(rv))) { - mValid = true; - return rv; - } - } - - return NS_OK; - } - - NS_IMETHOD - OnInputStreamReady(nsIAsyncInputStream* aStream) override - { - MOZ_ASSERT(aStream, "This cannot be one of ours."); - - MutexAutoLock lock(mMutex); - - // We failed during the Run(). - if (!mValid) { - return NS_OK; - } - - MOZ_ASSERT(mPendingStreams.Contains(aStream)); - mPendingStreams.RemoveElement(aStream); - - // The last asyncStream answered. We can inform nsMultiplexInputStream. - if (mPendingStreams.IsEmpty()) { - RefPtr runnable = new AsyncWaitRunnable(mStream); - return mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); - } - - return NS_OK; - } - - Mutex mMutex; - RefPtr mStream; - nsTArray> mPendingStreams; - nsCOMPtr mEventTarget; - bool mValid; -}; - -NS_IMPL_ISUPPORTS(AsyncStreamHelper, nsIInputStreamCallback) - -NS_IMETHODIMP -nsMultiplexInputStream::AsyncWait(nsIInputStreamCallback* aCallback, - uint32_t aFlags, - uint32_t aRequestedCount, - nsIEventTarget* aEventTarget) -{ - // When AsyncWait() is called, it's better to call AsyncWait() to any sub - // stream if they are valid nsIAsyncInputStream instances. In this way, when - // they all call OnInputStreamReady(), we can proceed with the Read(). - - MutexAutoLock lock(mLock); - - if (NS_FAILED(mStatus)) { - return mStatus; - } - - if (mAsyncWaitCallback && aCallback) { - return NS_ERROR_FAILURE; - } - - mAsyncWaitCallback = aCallback; - - if (!mAsyncWaitCallback) { - return NS_OK; - } - - nsTArray> asyncStreams; - for (uint32_t i = mCurrentStream; i < mStreams.Length(); ++i) { - nsCOMPtr asyncStream = - do_QueryInterface(mStreams.SafeElementAt(i, nullptr)); - if (asyncStream) { - asyncStreams.AppendElement(asyncStream); - } - } - - if (!aEventTarget) { - aEventTarget = SystemGroup::EventTargetFor(TaskCategory::Other); - } - - if (asyncStreams.IsEmpty()) { - RefPtr runnable = new AsyncWaitRunnable(this); - return aEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); - } - - return AsyncStreamHelper::Process(this, asyncStreams, aFlags, aRequestedCount, - aEventTarget); -} - -void -nsMultiplexInputStream::AsyncWaitCompleted() -{ - nsCOMPtr callback; - - { - MutexAutoLock lock(mLock); - - // The callback has been nullified in the meantime. - if (!mAsyncWaitCallback) { - return; - } - - mAsyncWaitCallback.swap(callback); - } - - callback->OnInputStreamReady(this); -} - nsresult nsMultiplexInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID, @@ -1016,7 +820,7 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone) return NS_ERROR_FAILURE; } - nsCOMPtr clone = new nsMultiplexInputStream(); + RefPtr clone = new nsMultiplexInputStream(); nsresult rv; uint32_t len = mStreams.Length(); @@ -1077,17 +881,3 @@ nsMultiplexInputStream::IsCloneable() const } return true; } - -bool -nsMultiplexInputStream::IsAsyncInputStream() const -{ - // nsMultiplexInputStream is nsIAsyncInputStream if at least 1 of the - // substream implements that interface. - for (uint32_t i = 0, len = mStreams.Length(); i < len; ++i) { - nsCOMPtr substream = do_QueryInterface(mStreams[i]); - if (substream) { - return true; - } - } - return false; -} From 9459a6e0cda7c13ad4afd687c503d47d426acaaf Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:59:52 +0200 Subject: [PATCH 058/131] Backed out changeset 7c902365910c (bug 929484) --- layout/reftests/table-background/reftest.list | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layout/reftests/table-background/reftest.list b/layout/reftests/table-background/reftest.list index 4ca60636ce4e4..68c831d27d7e9 100644 --- a/layout/reftests/table-background/reftest.list +++ b/layout/reftests/table-background/reftest.list @@ -47,8 +47,8 @@ fuzzy-if(d2d,1,16359) fuzzy-if(skiaContent,1,17000) == border-collapse-opacity-t fuzzy-if(d2d,1,5453) fuzzy-if(skiaContent,1,11000) == border-collapse-opacity-table-row.html border-collapse-opacity-table-row-ref.html fuzzy-if(d2d||skiaContent,1,60000) == border-collapse-opacity-table.html border-collapse-opacity-table-ref.html fuzzy-if(d2d,1,2478) fuzzy-if(skiaContent,1,2500) == border-separate-opacity-table-cell.html border-separate-opacity-table-cell-ref.html -== border-separate-opacity-table-column-group.html border-separate-opacity-table-column-group-ref.html # bug 424274 -== border-separate-opacity-table-column.html border-separate-opacity-table-column-ref.html # bug 424274 +fails-if(!stylo) == border-separate-opacity-table-column-group.html border-separate-opacity-table-column-group-ref.html # bug 424274 +fails-if(!stylo) == border-separate-opacity-table-column.html border-separate-opacity-table-column-ref.html # bug 424274 fuzzy-if(d2d,1,37170) fuzzy-if(skiaContent,1,38000) == border-separate-opacity-table-row-group.html border-separate-opacity-table-row-group-ref.html fuzzy-if(d2d,1,12390) fuzzy-if(skiaContent,1,13000) == border-separate-opacity-table-row.html border-separate-opacity-table-row-ref.html fuzzy-if(d2d||skiaContent,1,95000) == border-separate-opacity-table.html border-separate-opacity-table-ref.html From 5ed5457f1feb8c8003d894af68e41d178126b6e8 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:59:54 +0200 Subject: [PATCH 059/131] Backed out changeset 9c8ade5137ae (bug 929484) --- layout/painting/nsDisplayList.cpp | 48 +++++++------------------------ layout/painting/nsDisplayList.h | 32 --------------------- 2 files changed, 10 insertions(+), 70 deletions(-) diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 9f8a3a4cc60d4..03102e4d4fa7a 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -3214,12 +3214,7 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil // clip for the nsDisplayBackgroundImage inside. DisplayListClipState::AutoSaveRestore bgImageClip(aBuilder); bgImageClip.Clear(); - if (aSecondaryReferenceFrame) { - bgItem = new (aBuilder) nsDisplayTableBackgroundImage(bgData, - aSecondaryReferenceFrame); - } else { - bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData); - } + bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData); } if (aSecondaryReferenceFrame) { thisItemList.AppendNewToTop( @@ -3234,13 +3229,7 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil } } else { - if (aSecondaryReferenceFrame) { - thisItemList.AppendNewToTop( - new (aBuilder) nsDisplayTableBackgroundImage(bgData, - aSecondaryReferenceFrame)); - } else { - thisItemList.AppendNewToTop(new (aBuilder) nsDisplayBackgroundImage(bgData)); - } + thisItemList.AppendNewToTop(new (aBuilder) nsDisplayBackgroundImage(bgData)); } if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) { @@ -3346,7 +3335,7 @@ nsDisplayBackgroundImage::ShouldCreateOwnLayer(nsDisplayListBuilder* aBuilder, return WHENEVER_POSSIBLE; } - nsIFrame* backgroundStyleFrame = nsCSSRendering::FindBackgroundStyleFrame(StyleFrame()); + nsIFrame* backgroundStyleFrame = nsCSSRendering::FindBackgroundStyleFrame(mFrame); if (ActiveLayerTracker::IsBackgroundPositionAnimated(aBuilder, backgroundStyleFrame)) { return WHENEVER_POSSIBLE; @@ -3455,8 +3444,8 @@ bool nsDisplayBackgroundImage::CanBuildWebRenderDisplayItems() { return mBackgroundStyle->mImage.mLayers[mLayer].mClip != StyleGeometryBox::Text && - nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(*StyleFrame()->PresContext(), - StyleFrame(), + nsCSSRendering::CanBuildWebRenderDisplayItemsForStyleImageLayer(*mFrame->PresContext(), + mFrame, mBackgroundStyle, mLayer); } @@ -3467,9 +3456,9 @@ nsDisplayBackgroundImage::CreateWebRenderCommands(wr::DisplayListBuilder& aBuild WebRenderDisplayItemLayer* aLayer) { nsCSSRendering::PaintBGParams params = - nsCSSRendering::PaintBGParams::ForSingleLayer(*StyleFrame()->PresContext(), + nsCSSRendering::PaintBGParams::ForSingleLayer(*mFrame->PresContext(), mVisibleRect, mBackgroundRect, - StyleFrame(), 0, mLayer, + mFrame, 0, mLayer, CompositionOp::OP_OVER); params.bgClipRect = &mBounds; @@ -3638,15 +3627,15 @@ nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder, StyleGeometryBox clip = mBackgroundStyle->mImage.mLayers[mLayer].mClip; if (clip == StyleGeometryBox::Text) { - if (!GenerateAndPushTextMask(StyleFrame(), aCtx, mBackgroundRect, aBuilder)) { + if (!GenerateAndPushTextMask(mFrame, aCtx, mBackgroundRect, aBuilder)) { return; } } nsCSSRendering::PaintBGParams params = - nsCSSRendering::PaintBGParams::ForSingleLayer(*StyleFrame()->PresContext(), + nsCSSRendering::PaintBGParams::ForSingleLayer(*mFrame->PresContext(), aBounds, mBackgroundRect, - StyleFrame(), flags, mLayer, + mFrame, flags, mLayer, CompositionOp::OP_OVER); params.bgClipRect = aClipRect; image::DrawResult result = @@ -3741,23 +3730,6 @@ nsDisplayBackgroundImage::GetPerFrameKey() nsDisplayItem::GetPerFrameKey(); } -nsDisplayTableBackgroundImage::nsDisplayTableBackgroundImage(const InitData& aData, - nsIFrame* aCellFrame) - : nsDisplayBackgroundImage(aData) - , mStyleFrame(aData.frame) - , mTableType(GetTableTypeFromFrame(mStyleFrame)) -{ - mFrame = aCellFrame; -} - -bool -nsDisplayTableBackgroundImage::IsInvalid(nsRect& aRect) -{ - bool result = mStyleFrame ? mStyleFrame->IsInvalid(aRect) : false; - aRect += ToReferenceFrame(); - return result; -} - nsDisplayThemedBackground::nsDisplayThemedBackground(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, const nsRect& aBackgroundRect) diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index ff91e770d9f34..7d7e85ec87d02 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -3150,8 +3150,6 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { void PaintInternal(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx, const nsRect& aBounds, nsRect* aClipRect); - virtual nsIFrame* StyleFrame() { return mFrame; } - // Determine whether we want to be separated into our own layer, independent // of whether this item can actually be layerized. enum ImageLayerization { @@ -3197,36 +3195,6 @@ static_assert( "TableType cannot fit with TableTypeBits::COUNT"); TableType GetTableTypeFromFrame(nsIFrame* aFrame); -/** - * A display item to paint background image for table. For table parts, such - * as row, row group, col, col group, when drawing its background, we'll - * create separate background image display item for its containning cell. - * Those background image display items will reference to same DisplayItemData - * if we keep the mFrame point to cell's ancestor frame. We don't want to this - * happened bacause share same DisplatItemData will cause many bugs. So that - * we let mFrame point to cell frame and store the table type of the ancestor - * frame. And use mFrame and table type as key to generate DisplayItemData to - * avoid sharing DisplayItemData. - * - * Also store ancestor frame as mStyleFrame for all rendering informations. - */ -class nsDisplayTableBackgroundImage : public nsDisplayBackgroundImage { -public: - nsDisplayTableBackgroundImage(const InitData& aInitData, nsIFrame* aCellFrame); - - virtual uint32_t GetPerFrameKey() override { - return (static_cast(mTableType) << nsDisplayItem::TYPE_BITS) | - nsDisplayItem::GetPerFrameKey(); - } - - virtual bool IsInvalid(nsRect& aRect) override; -protected: - virtual nsIFrame* StyleFrame() override { return mStyleFrame; } - - nsIFrame* mStyleFrame; - TableType mTableType; -}; - /** * A display item to paint the native theme background for a frame. */ From 1a13470209bc1343b56d28a379808995625d5c88 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:59:57 +0200 Subject: [PATCH 060/131] Backed out changeset e41fa1543360 (bug 929484) --- layout/painting/nsDisplayList.cpp | 72 ++----------------------------- layout/painting/nsDisplayList.h | 45 +------------------ layout/tables/nsTableFrame.cpp | 6 +-- 3 files changed, 7 insertions(+), 116 deletions(-) diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 03102e4d4fa7a..3789074d9b209 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -83,8 +83,6 @@ #include "nsCSSProps.h" #include "nsPluginFrame.h" #include "nsSVGMaskFrame.h" -#include "nsTableCellFrame.h" -#include "nsTableColFrame.h" #include "ClientLayerManager.h" #include "mozilla/layers/WebRenderBridgeChild.h" #include "mozilla/layers/WebRenderLayerManager.h" @@ -3065,8 +3063,7 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil nsDisplayList* aList, bool aAllowWillPaintBorderOptimization, nsStyleContext* aStyleContext, - const nsRect& aBackgroundOriginRect, - nsIFrame* aSecondaryReferenceFrame) + const nsRect& aBackgroundOriginRect) { nsStyleContext* bgSC = aStyleContext; const nsStyleBackground* bg = nullptr; @@ -3216,17 +3213,8 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil bgImageClip.Clear(); bgItem = new (aBuilder) nsDisplayBackgroundImage(bgData); } - if (aSecondaryReferenceFrame) { - thisItemList.AppendNewToTop( - nsDisplayTableFixedPosition::CreateForFixedBackground(aBuilder, - aSecondaryReferenceFrame, - bgItem, - i, - aFrame)); - } else { - thisItemList.AppendNewToTop( - nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, aFrame, bgItem, i)); - } + thisItemList.AppendNewToTop( + nsDisplayFixedPosition::CreateForFixedBackground(aBuilder, aFrame, bgItem, i)); } else { thisItemList.AppendNewToTop(new (aBuilder) nsDisplayBackgroundImage(bgData)); @@ -6446,60 +6434,6 @@ bool nsDisplayFixedPosition::TryMerge(nsDisplayItem* aItem) { return true; } -TableType -GetTableTypeFromFrame(nsIFrame* aFrame) -{ - if (aFrame->IsTableFrame()) { - return TableType::TABLE; - } - - if (aFrame->IsTableColFrame()) { - return TableType::TABLE_COL; - } - - if (aFrame->IsTableColGroupFrame()) { - return TableType::TABLE_COL_GROUP; - } - - if (aFrame->IsTableRowFrame()) { - return TableType::TABLE_ROW; - } - - if (aFrame->IsTableRowGroupFrame()) { - return TableType::TABLE_ROW_GROUP; - } - - if (aFrame->IsTableCellFrame()) { - return TableType::TABLE_CELL; - } - - MOZ_ASSERT_UNREACHABLE("Invalid frame."); - return TableType::TABLE; -} - -nsDisplayTableFixedPosition::nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame, - nsDisplayList* aList, - uint32_t aIndex, - nsIFrame* aAncestorFrame) - : nsDisplayFixedPosition(aBuilder, aFrame, aList, aIndex) - , mTableType(GetTableTypeFromFrame(aAncestorFrame)) -{ -} - -/* static */ nsDisplayTableFixedPosition* -nsDisplayTableFixedPosition::CreateForFixedBackground(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame, - nsDisplayBackgroundImage* aImage, - uint32_t aIndex, - nsIFrame* aAncestorFrame) -{ - nsDisplayList temp; - temp.AppendToTop(aImage); - - return new (aBuilder) nsDisplayTableFixedPosition(aBuilder, aFrame, &temp, aIndex + 1, aAncestorFrame); -} - nsDisplayStickyPosition::nsDisplayStickyPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index 7d7e85ec87d02..e2f441ded8d0e 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -3072,8 +3072,7 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { nsDisplayList* aList, bool aAllowWillPaintBorderOptimization = true, nsStyleContext* aStyleContext = nullptr, - const nsRect& aBackgroundOriginRect = nsRect(), - nsIFrame* aSecondaryReferenceFrame = nullptr); + const nsRect& aBackgroundOriginRect = nsRect()); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager, @@ -3175,25 +3174,6 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { bool mShouldFixToViewport; }; -enum class TableType : uint8_t { - TABLE, - TABLE_COL, - TABLE_COL_GROUP, - TABLE_ROW, - TABLE_ROW_GROUP, - TABLE_CELL, - - TABLE_TYPE_MAX -}; - -enum class TableTypeBits : uint8_t { - COUNT = 3 -}; - -static_assert( - static_cast(TableType::TABLE_TYPE_MAX) < (1 << (static_cast(TableTypeBits::COUNT) + 1)), - "TableType cannot fit with TableTypeBits::COUNT"); -TableType GetTableTypeFromFrame(nsIFrame* aFrame); /** * A display item to paint the native theme background for a frame. @@ -4143,7 +4123,7 @@ class nsDisplayFixedPosition : public nsDisplayOwnLayer { return mAnimatedGeometryRootForScrollMetadata; } -protected: +private: // For background-attachment:fixed nsDisplayFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList, uint32_t aIndex); @@ -4154,27 +4134,6 @@ class nsDisplayFixedPosition : public nsDisplayOwnLayer { bool mIsFixedBackground; }; -class nsDisplayTableFixedPosition : public nsDisplayFixedPosition -{ -public: - static nsDisplayTableFixedPosition* CreateForFixedBackground(nsDisplayListBuilder* aBuilder, - nsIFrame* aFrame, - nsDisplayBackgroundImage* aImage, - uint32_t aIndex, - nsIFrame* aAncestorFrame); - - virtual uint32_t GetPerFrameKey() override { - return (mIndex << (nsDisplayItem::TYPE_BITS + static_cast(TableTypeBits::COUNT))) | - (static_cast(mTableType) << nsDisplayItem::TYPE_BITS) | - nsDisplayItem::GetPerFrameKey(); - } -protected: - nsDisplayTableFixedPosition(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, - nsDisplayList* aList, uint32_t aIndex, nsIFrame* aAncestorFrame); - - TableType mTableType; -}; - /** * This creates an empty scrollable layer. It has no child layers. * It is used to record the existence of a scrollable frame in the layer diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 0cbc37ff13a04..2d78bb3fe5ad2 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1311,8 +1311,7 @@ PaintRowBackground(nsTableRowFrame* aRow, nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect, aLists.BorderBackground(), true, nullptr, - aFrame->GetRectRelativeToSelf(), - cell); + aFrame->GetRectRelativeToSelf()); } } @@ -1344,8 +1343,7 @@ PaintRowGroupBackgroundByColIdx(nsTableRowGroupFrame* aRowGroup, nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect, aLists.BorderBackground(), true, nullptr, - aFrame->GetRectRelativeToSelf(), - cell); + aFrame->GetRectRelativeToSelf()); } } } From b2ac23b60047f3c0d8105f44d51c9dd20ffcfeed Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 16:59:59 +0200 Subject: [PATCH 061/131] Backed out changeset 3dc8c2aea9bf (bug 929484) --- layout/painting/nsDisplayItemTypesList.h | 3 + layout/tables/moz.build | 1 + layout/tables/nsTableCellFrame.cpp | 14 + layout/tables/nsTableCellFrame.h | 5 + layout/tables/nsTableFrame.cpp | 84 +++ layout/tables/nsTableFrame.h | 10 + layout/tables/nsTablePainter.cpp | 695 +++++++++++++++++++++++ layout/tables/nsTablePainter.h | 268 +++++++++ layout/tables/nsTableRowFrame.cpp | 39 ++ layout/tables/nsTableRowFrame.h | 1 + layout/tables/nsTableRowGroupFrame.cpp | 40 ++ layout/tables/nsTableRowGroupFrame.h | 1 + 12 files changed, 1161 insertions(+) create mode 100644 layout/tables/nsTablePainter.cpp create mode 100644 layout/tables/nsTablePainter.h diff --git a/layout/painting/nsDisplayItemTypesList.h b/layout/painting/nsDisplayItemTypesList.h index 71036dd05cc58..be3950e4d8f4e 100644 --- a/layout/painting/nsDisplayItemTypesList.h +++ b/layout/painting/nsDisplayItemTypesList.h @@ -56,6 +56,9 @@ DECLARE_DISPLAY_ITEM_TYPE(SVG_GEOMETRY) DECLARE_DISPLAY_ITEM_TYPE(SVG_TEXT) DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_BACKGROUND) DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_SELECTION) +DECLARE_DISPLAY_ITEM_TYPE(TABLE_ROW_BACKGROUND) +DECLARE_DISPLAY_ITEM_TYPE(TABLE_ROW_GROUP_BACKGROUND) +DECLARE_DISPLAY_ITEM_TYPE(TABLE_BORDER_BACKGROUND) DECLARE_DISPLAY_ITEM_TYPE(TABLE_BORDER_COLLAPSE) DECLARE_DISPLAY_ITEM_TYPE(TEXT) DECLARE_DISPLAY_ITEM_TYPE(TEXT_OVERFLOW) diff --git a/layout/tables/moz.build b/layout/tables/moz.build index fba649593d4f0..1a23480a0120b 100644 --- a/layout/tables/moz.build +++ b/layout/tables/moz.build @@ -21,6 +21,7 @@ UNIFIED_SOURCES += [ 'nsTableColFrame.cpp', 'nsTableColGroupFrame.cpp', 'nsTableFrame.cpp', + 'nsTablePainter.cpp', 'nsTableRowFrame.cpp', 'nsTableRowGroupFrame.cpp', 'nsTableWrapperFrame.cpp', diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index 8084eff709ea5..09090847bed89 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -12,6 +12,7 @@ #include "nsTableColFrame.h" #include "nsTableRowFrame.h" #include "nsTableRowGroupFrame.h" +#include "nsTablePainter.h" #include "nsStyleContext.h" #include "nsStyleConsts.h" #include "nsPresContext.h" @@ -385,6 +386,19 @@ nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext, return nsCSSRendering::PaintStyleImageLayer(params, aRenderingContext); } +// Called by nsTablePainter +DrawResult +nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, nsPoint aPt, + uint32_t aFlags) +{ + if (!StyleVisibility()->IsVisible()) { + return DrawResult::SUCCESS; + } + + return PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags); +} + nsresult nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame, nsDisplayListBuilder* aBuilder, diff --git a/layout/tables/nsTableCellFrame.h b/layout/tables/nsTableCellFrame.h index 296dc03a682d3..1fda36b318033 100644 --- a/layout/tables/nsTableCellFrame.h +++ b/layout/tables/nsTableCellFrame.h @@ -112,6 +112,11 @@ class nsTableCellFrame : public nsContainerFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; + DrawResult PaintCellBackground(nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, nsPoint aPt, + uint32_t aFlags); + + virtual nsresult ProcessBorders(nsTableFrame* aFrame, nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists); diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 2d78bb3fe5ad2..1fc52c66e92d8 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -23,6 +23,7 @@ #include "nsTableRowFrame.h" #include "nsTableRowGroupFrame.h" #include "nsTableWrapperFrame.h" +#include "nsTablePainter.h" #include "BasicTableLayoutStrategy.h" #include "FixedTableLayoutStrategy.h" @@ -1277,6 +1278,36 @@ nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder, static_cast(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt); } +class nsDisplayTableBorderBackground : public nsDisplayTableItem { +public: + nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, + nsTableFrame* aFrame, + bool aDrawsBackground) : + nsDisplayTableItem(aBuilder, aFrame, aDrawsBackground) { + MOZ_COUNT_CTOR(nsDisplayTableBorderBackground); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayTableBorderBackground() { + MOZ_COUNT_DTOR(nsDisplayTableBorderBackground); + } +#endif + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) override; + NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND) +}; + +void +nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + DrawResult result = static_cast(mFrame)-> + PaintTableBorderBackground(aBuilder, *aCtx, mVisibleRect, + ToReferenceFrame()); + + nsDisplayTableItemGeometry::UpdateDrawResult(this, result); +} + /* static */ void nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) @@ -1485,6 +1516,59 @@ nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const return GetOuterBCBorder(wm).GetPhysicalMargin(wm); } +// XXX We don't put the borders and backgrounds in tree order like we should. +// That requires some major surgery which we aren't going to do right now. +DrawResult +nsTableFrame::PaintTableBorderBackground(nsDisplayListBuilder* aBuilder, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsPoint aPt) +{ + nsPresContext* presContext = PresContext(); + + uint32_t bgFlags = aBuilder->GetBackgroundPaintFlags(); + PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages() + ? PaintBorderFlags::SYNC_DECODE_IMAGES + : PaintBorderFlags(); + + TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table, + presContext, aRenderingContext, + aDirtyRect, aPt, bgFlags); + nsMargin deflate = GetDeflationForBackground(presContext); + // If 'deflate' is (0,0,0,0) then we'll paint the table background + // in a separate display item, so don't do it here. + DrawResult result = + painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0)); + + if (StyleVisibility()->IsVisible()) { + if (!IsBorderCollapse()) { + Sides skipSides = GetSkipSides(); + nsRect rect(aPt, mRect.Size()); + + result &= + nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, + aDirtyRect, rect, mStyleContext, + borderFlags, skipSides); + } else { + DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); + + gfxPoint devPixelOffset = + nsLayoutUtils::PointToGfxPoint(aPt, + PresContext()->AppUnitsPerDevPixel()); + + // XXX we should probably get rid of this translation at some stage + // But that would mean modifying PaintBCBorders, ugh + AutoRestoreTransform autoRestoreTransform(drawTarget); + drawTarget->SetTransform( + drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset))); + + PaintBCBorders(*drawTarget, aDirtyRect - aPt); + } + } + + return result; +} + nsIFrame::LogicalSides nsTableFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const { diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index a53f52b2c1468..a15015431c766 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -266,6 +266,16 @@ class nsTableFrame : public nsContainerFrame const nsRect& aDirtyRect, const nsDisplayListSet& aLists) override; + /** + * Paint the background of the table and its parts (column groups, + * columns, row groups, rows, and cells), and the table border, and all + * internal borders if border-collapse is on. + */ + DrawResult PaintTableBorderBackground(nsDisplayListBuilder* aBuilder, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + nsPoint aPt); + /** Get the outer half (i.e., the part outside the height and width of * the table) of the largest segment (?) of border-collapsed border on * the table on each side, or 0 for non border-collapsed tables. diff --git a/layout/tables/nsTablePainter.cpp b/layout/tables/nsTablePainter.cpp new file mode 100644 index 0000000000000..3acff80963ead --- /dev/null +++ b/layout/tables/nsTablePainter.cpp @@ -0,0 +1,695 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTableFrame.h" +#include "nsTableRowGroupFrame.h" +#include "nsTableRowFrame.h" +#include "nsTableColGroupFrame.h" +#include "nsTableColFrame.h" +#include "nsTableCellFrame.h" +#include "nsTablePainter.h" +#include "nsCSSRendering.h" +#include "nsDisplayList.h" +#include "mozilla/WritingModes.h" + +/* ~*~ Table Background Painting ~*~ + + Mozilla's Table Background painting follows CSS2.1:17.5.1 + That section does not, however, describe the effect of + borders on background image positioning. What we do is: + + - in separate borders, the borders are passed in so that + their width figures in image positioning, even for rows/cols, which + don't have visible borders. This is done to allow authors + to position row backgrounds by, for example, aligning the + top left corner with the top left padding corner of the + top left table cell in the row in cases where all cells + have consistent border widths. If we didn't honor these + invisible borders, there would be no way to align + backgrounds with the padding edges, and designs would be + lost underneath the border. + + - in collapsing borders, because the borders collapse, we + use the -continuous border- width to synthesize a border + style and pass that in instead of using the element's + assigned style directly. + + The continuous border on a given edge of an element is + the collapse of all borders guaranteed to be continuous + along that edge. Cell borders are ignored (because, for + example, setting a thick border on the leftmost cell + should not shift the row background over; this way a + striped background set on will line up across rows + even if the cells are assigned arbitrary border widths. + + For example, the continuous border on the top edge of a + row group is the collapse of any row group, row, and + table borders involved. (The first row group's top would + be [table-top + row group top + first row top]. It's bottom + would be [row group bottom + last row bottom + next row + top + next row group top].) + The top edge of a column group likewise includes the + table top, row group top, and first row top borders. However, + it *also* includes its own top border, since that is guaranteed + to be continuous. It does not include column borders because + those are not guaranteed to be continuous: there may be two + columns with different borders in a single column group. + + An alternative would be to define the continuous border as + [table? + row group + row] for horizontal + [table? + col group + col] for vertical + This makes it easier to line up backgrounds across elements + despite varying border widths, but it does not give much + flexibility in aligning /to/ those border widths. +*/ + + +/* ~*~ TableBackgroundPainter ~*~ + + The TableBackgroundPainter is created and destroyed in one painting call. + Its principal function is PaintTable, which paints all table element + backgrounds. The initial code in that method sets up an array of column + data that caches the background styles and the border sizes for the + columns and colgroups in TableBackgroundData structs in mCols. Data for + BC borders are calculated and stashed in a synthesized border style struct + in the data struct since collapsed borders aren't the same width as style- + assigned borders. The data struct optimizes by only doing this if there's + an image background; otherwise we don't care. //XXX should also check background-origin + The class then loops through the row groups, rows, and cells. At the cell + level, it paints the backgrounds, one over the other, inside the cell rect. + + The exception to this pattern is when a table element creates a (pseudo) + stacking context. Elements with stacking contexts (e.g., 'opacity' applied) + are passed through, which means their data (and their + descendants' data) are not cached. The full loop is still executed, however, + so that underlying layers can get painted at the cell level. + + The TableBackgroundPainter is then destroyed. + + Elements with stacking contexts set up their own painter to finish the + painting process, since they were skipped. They call the appropriate + sub-part of the loop (e.g. PaintRow) which will paint the frame and + descendants. + + XXX views are going + */ + +using namespace mozilla; +using namespace mozilla::image; + +TableBackgroundPainter::TableBackgroundData::TableBackgroundData() + : mFrame(nullptr) + , mVisible(false) + , mUsesSynthBorder(false) +{ +} + +TableBackgroundPainter::TableBackgroundData::TableBackgroundData(nsIFrame* aFrame) + : mFrame(aFrame) + , mRect(aFrame->GetRect()) + , mVisible(mFrame->IsVisibleForPainting()) + , mUsesSynthBorder(false) +{ +} + +inline bool +TableBackgroundPainter::TableBackgroundData::ShouldSetBCBorder() const +{ + /* we only need accurate border data when positioning background images*/ + if (!mVisible) { + return false; + } + + const nsStyleImageLayers& layers = mFrame->StyleBackground()->mImage; + NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, layers) { + if (!layers.mLayers[i].mImage.IsEmpty()) + return true; + } + return false; +} + +void +TableBackgroundPainter::TableBackgroundData::SetBCBorder(const nsMargin& aBorder) +{ + mUsesSynthBorder = true; + mSynthBorderWidths = aBorder; +} + +nsStyleBorder +TableBackgroundPainter::TableBackgroundData::StyleBorder(const nsStyleBorder& aZeroBorder) const +{ + MOZ_ASSERT(mVisible, "Don't call StyleBorder on an invisible TableBackgroundData"); + + if (mUsesSynthBorder) { + nsStyleBorder result = aZeroBorder; + NS_FOR_CSS_SIDES(side) { + result.SetBorderWidth(side, mSynthBorderWidths.Side(side)); + } + return result; + } + + MOZ_ASSERT(mFrame); + + return *mFrame->StyleBorder(); +} + +TableBackgroundPainter::TableBackgroundPainter(nsTableFrame* aTableFrame, + Origin aOrigin, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + const nsPoint& aRenderPt, + uint32_t aBGPaintFlags) + : mPresContext(aPresContext), + mRenderingContext(aRenderingContext), + mRenderPt(aRenderPt), + mDirtyRect(aDirtyRect), + mOrigin(aOrigin), + mZeroBorder(aPresContext), + mBGPaintFlags(aBGPaintFlags) +{ + MOZ_COUNT_CTOR(TableBackgroundPainter); + + NS_FOR_CSS_SIDES(side) { + mZeroBorder.SetBorderStyle(side, NS_STYLE_BORDER_STYLE_SOLID); + mZeroBorder.SetBorderWidth(side, 0); + } + + mIsBorderCollapse = aTableFrame->IsBorderCollapse(); +#ifdef DEBUG + mCompatMode = mPresContext->CompatibilityMode(); +#endif + mNumCols = aTableFrame->GetColCount(); +} + +TableBackgroundPainter::~TableBackgroundPainter() +{ + MOZ_COUNT_DTOR(TableBackgroundPainter); +} + +DrawResult +TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame, + nsTableRowGroupFrame* aFirstRowGroup, + nsTableRowGroupFrame* aLastRowGroup, + const nsMargin& aDeflate) +{ + MOZ_ASSERT(aTableFrame, "null frame"); + TableBackgroundData tableData(aTableFrame); + tableData.mRect.MoveTo(0,0); //using table's coords + tableData.mRect.Deflate(aDeflate); + WritingMode wm = aTableFrame->GetWritingMode(); + if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) { + if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) { + //only handle non-degenerate tables; we need a more robust BC model + //to make degenerate tables' borders reasonable to deal with + LogicalMargin border(wm); + LogicalMargin tempBorder(wm); + nsTableColFrame* colFrame = aTableFrame->GetColFrame(mNumCols - 1); + if (colFrame) { + colFrame->GetContinuousBCBorderWidth(wm, tempBorder); + } + border.IEnd(wm) = tempBorder.IEnd(wm); + + aLastRowGroup->GetContinuousBCBorderWidth(wm, tempBorder); + border.BEnd(wm) = tempBorder.BEnd(wm); + + nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow(); + if (rowFrame) { + rowFrame->GetContinuousBCBorderWidth(wm, tempBorder); + border.BStart(wm) = tempBorder.BStart(wm); + } + + border.IStart(wm) = aTableFrame->GetContinuousIStartBCBorderWidth(); + + tableData.SetBCBorder(border.GetPhysicalMargin(wm)); + } + } + + DrawResult result = DrawResult::SUCCESS; + + if (tableData.IsVisible()) { + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, + mDirtyRect, + tableData.mRect + mRenderPt, + tableData.mFrame, + mBGPaintFlags); + + result &= + nsCSSRendering::PaintStyleImageLayerWithSC(params, mRenderingContext, + tableData.mFrame->StyleContext(), + tableData.StyleBorder(mZeroBorder)); + } + + return result; +} + +void +TableBackgroundPainter::TranslateContext(nscoord aDX, + nscoord aDY) +{ + mRenderPt += nsPoint(aDX, aDY); + for (auto& col : mCols) { + col.mCol.mRect.MoveBy(-aDX, -aDY); + } + for (auto& colGroup : mColGroups) { + colGroup.mRect.MoveBy(-aDX, -aDY); + } +} + +TableBackgroundPainter::ColData::ColData(nsIFrame* aFrame, TableBackgroundData& aColGroupBGData) + : mCol(aFrame) + , mColGroup(aColGroupBGData) +{ +} + +DrawResult +TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame, + const nsMargin& aDeflate, + bool aPaintTableBackground) +{ + NS_PRECONDITION(aTableFrame, "null table frame"); + + nsTableFrame::RowGroupArray rowGroups; + aTableFrame->OrderRowGroups(rowGroups); + WritingMode wm = aTableFrame->GetWritingMode(); + + DrawResult result = DrawResult::SUCCESS; + + if (rowGroups.Length() < 1) { //degenerate case + if (aPaintTableBackground) { + result &= PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0)); + } + /* No cells; nothing else to paint */ + return result; + } + + if (aPaintTableBackground) { + result &= + PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1], + aDeflate); + } + + /*Set up column background/border data*/ + if (mNumCols > 0) { + nsFrameList& colGroupList = aTableFrame->GetColGroups(); + NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup"); + + // Collect all col group frames first so that we know how many there are. + nsTArray colGroupFrames; + for (nsTableColGroupFrame* cgFrame = static_cast(colGroupList.FirstChild()); + cgFrame; cgFrame = static_cast(cgFrame->GetNextSibling())) { + + if (cgFrame->GetColCount() < 1) { + //No columns, no cells, so no need for data + continue; + } + colGroupFrames.AppendElement(cgFrame); + } + + // Ensure that mColGroups won't reallocate during the loop below, because + // we grab references to its contents and need those to stay valid until + // mColGroups is destroyed as part of TablePainter destruction. + mColGroups.SetCapacity(colGroupFrames.Length()); + + LogicalMargin border(wm); + /* BC iStart borders aren't stored on cols, but the previous column's + iEnd border is the next one's iStart border.*/ + //Start with table's iStart border. + nscoord lastIStartBorder = aTableFrame->GetContinuousIStartBCBorderWidth(); + + for (nsTableColGroupFrame* cgFrame : colGroupFrames) { + /*Create data struct for column group*/ + TableBackgroundData& cgData = *mColGroups.AppendElement(TableBackgroundData(cgFrame)); + if (mIsBorderCollapse && cgData.ShouldSetBCBorder()) { + border.IStart(wm) = lastIStartBorder; + cgFrame->GetContinuousBCBorderWidth(wm, border); + cgData.SetBCBorder(border.GetPhysicalMargin(wm)); + } + + /*Loop over columns in this colgroup*/ + for (nsTableColFrame* col = cgFrame->GetFirstColumn(); col; + col = static_cast(col->GetNextSibling())) { + MOZ_ASSERT(size_t(col->GetColIndex()) == mCols.Length()); + // Store a reference to the colGroup in the ColData element. + ColData& colData = *mCols.AppendElement(ColData(col, cgData)); + //Bring column mRect into table's coord system + colData.mCol.mRect.MoveBy(cgData.mRect.x, cgData.mRect.y); + if (mIsBorderCollapse) { + border.IStart(wm) = lastIStartBorder; + lastIStartBorder = col->GetContinuousBCBorderWidth(wm, border); + if (colData.mCol.ShouldSetBCBorder()) { + colData.mCol.SetBCBorder(border.GetPhysicalMargin(wm)); + } + } + } + } + } + + for (uint32_t i = 0; i < rowGroups.Length(); i++) { + nsTableRowGroupFrame* rg = rowGroups[i]; + TableBackgroundData rowGroupBGData(rg); + // Need to compute the right rect via GetOffsetTo, since the row + // group may not be a child of the table. + rowGroupBGData.mRect.MoveTo(rg->GetOffsetTo(aTableFrame)); + + // We have to draw backgrounds not only within the overflow region of this + // row group, but also possibly (in the case of column / column group + // backgrounds) at its pre-relative-positioning location. + nsRect rgVisualOverflow = rg->GetVisualOverflowRectRelativeToSelf(); + nsRect rgOverflowRect = rgVisualOverflow + rg->GetPosition(); + nsRect rgNormalRect = rgVisualOverflow + rg->GetNormalPosition(); + + if (rgOverflowRect.Union(rgNormalRect).Intersects(mDirtyRect - mRenderPt)) { + result &= + PaintRowGroup(rg, rowGroupBGData, rg->IsPseudoStackingContextFromStyle()); + } + } + + return result; +} + +DrawResult +TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame) +{ + return PaintRowGroup(aFrame, TableBackgroundData(aFrame), false); +} + +DrawResult +TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame, + TableBackgroundData aRowGroupBGData, + bool aPassThrough) +{ + MOZ_ASSERT(aFrame, "null frame"); + + nsTableRowFrame* firstRow = aFrame->GetFirstRow(); + WritingMode wm = aFrame->GetWritingMode(); + + /* Load row group data */ + if (aPassThrough) { + aRowGroupBGData.MakeInvisible(); + } else { + if (mIsBorderCollapse && aRowGroupBGData.ShouldSetBCBorder()) { + LogicalMargin border(wm); + if (firstRow) { + //pick up first row's bstart border (= rg bstart border) + firstRow->GetContinuousBCBorderWidth(wm, border); + /* (row group doesn't store its bstart border) */ + } + //overwrite sides+bottom borders with rg's own + aFrame->GetContinuousBCBorderWidth(wm, border); + aRowGroupBGData.SetBCBorder(border.GetPhysicalMargin(wm)); + } + aPassThrough = !aRowGroupBGData.IsVisible(); + } + + /* translate everything into row group coord system*/ + if (eOrigin_TableRowGroup != mOrigin) { + TranslateContext(aRowGroupBGData.mRect.x, aRowGroupBGData.mRect.y); + } + nsRect rgRect = aRowGroupBGData.mRect; + aRowGroupBGData.mRect.MoveTo(0, 0); + + /* Find the right row to start with */ + + // Note that mDirtyRect - mRenderPt is guaranteed to be in the row + // group's coordinate system here, so passing its .y to + // GetFirstRowContaining is ok. + nscoord overflowAbove; + nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &overflowAbove); + + // Sadly, it seems like there may be non-row frames in there... or something? + // There are certainly null-checks in GetFirstRow() and GetNextRow(). :( + while (cursor && !cursor->IsTableRowFrame()) { + cursor = cursor->GetNextSibling(); + } + + // It's OK if cursor is null here. + nsTableRowFrame* row = static_cast(cursor); + if (!row) { + // No useful cursor; just start at the top. Don't bother to set up a + // cursor; if we've gotten this far then we've already built the display + // list for the rowgroup, so not having a cursor means that there's some + // good reason we don't have a cursor and we shouldn't create one here. + row = firstRow; + } + + DrawResult result = DrawResult::SUCCESS; + + /* Finally paint */ + for (; row; row = row->GetNextRow()) { + TableBackgroundData rowBackgroundData(row); + + // Be sure to consider our positions both pre- and post-relative + // positioning, since we potentially need to paint at both places. + nscoord rowY = std::min(rowBackgroundData.mRect.y, row->GetNormalPosition().y); + + // Intersect wouldn't handle rowspans. + if (cursor && + (mDirtyRect.YMost() - mRenderPt.y) <= (rowY - overflowAbove)) { + // All done; cells originating in later rows can't intersect mDirtyRect. + break; + } + + result &= + PaintRow(row, aRowGroupBGData, rowBackgroundData, + aPassThrough || row->IsPseudoStackingContextFromStyle()); + } + + /* translate back into table coord system */ + if (eOrigin_TableRowGroup != mOrigin) { + TranslateContext(-rgRect.x, -rgRect.y); + } + + return result; +} + +DrawResult +TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame) +{ + return PaintRow(aFrame, TableBackgroundData(), TableBackgroundData(aFrame), false); +} + +DrawResult +TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame, + const TableBackgroundData& aRowGroupBGData, + TableBackgroundData aRowBGData, + bool aPassThrough) +{ + MOZ_ASSERT(aFrame, "null frame"); + + /* Load row data */ + WritingMode wm = aFrame->GetWritingMode(); + if (aPassThrough) { + aRowBGData.MakeInvisible(); + } else { + if (mIsBorderCollapse && aRowBGData.ShouldSetBCBorder()) { + LogicalMargin border(wm); + nsTableRowFrame* nextRow = aFrame->GetNextRow(); + if (nextRow) { //outer bStart after us is inner bEnd for us + border.BEnd(wm) = nextRow->GetOuterBStartContBCBorderWidth(); + } + else { //acquire rg's bEnd border + nsTableRowGroupFrame* rowGroup = static_cast(aFrame->GetParent()); + rowGroup->GetContinuousBCBorderWidth(wm, border); + } + //get the rest of the borders; will overwrite all but bEnd + aFrame->GetContinuousBCBorderWidth(wm, border); + + aRowBGData.SetBCBorder(border.GetPhysicalMargin(wm)); + } + aPassThrough = !aRowBGData.IsVisible(); + } + + /* Translate */ + if (eOrigin_TableRow == mOrigin) { + /* If we originate from the row, then make the row the origin. */ + aRowBGData.mRect.MoveTo(0, 0); + } + //else: Use row group's coord system -> no translation necessary + + DrawResult result = DrawResult::SUCCESS; + + for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) { + nsRect cellBGRect, rowBGRect, rowGroupBGRect, colBGRect; + ComputeCellBackgrounds(cell, aRowGroupBGData, aRowBGData, + cellBGRect, rowBGRect, + rowGroupBGRect, colBGRect); + + // Find the union of all the cell background layers. + nsRect combinedRect(cellBGRect); + combinedRect.UnionRect(combinedRect, rowBGRect); + combinedRect.UnionRect(combinedRect, rowGroupBGRect); + combinedRect.UnionRect(combinedRect, colBGRect); + + if (combinedRect.Intersects(mDirtyRect)) { + bool passCell = aPassThrough || cell->IsPseudoStackingContextFromStyle(); + result &= + PaintCell(cell, aRowGroupBGData, aRowBGData, cellBGRect, rowBGRect, + rowGroupBGRect, colBGRect, passCell); + } + } + + return result; +} + +DrawResult +TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell, + const TableBackgroundData& aRowGroupBGData, + const TableBackgroundData& aRowBGData, + nsRect& aCellBGRect, + nsRect& aRowBGRect, + nsRect& aRowGroupBGRect, + nsRect& aColBGRect, + bool aPassSelf) +{ + MOZ_ASSERT(aCell, "null frame"); + + const nsStyleTableBorder* cellTableStyle; + cellTableStyle = aCell->StyleTableBorder(); + if (NS_STYLE_TABLE_EMPTY_CELLS_SHOW != cellTableStyle->mEmptyCells && + aCell->GetContentEmpty() && !mIsBorderCollapse) { + return DrawResult::SUCCESS; + } + + int32_t colIndex; + aCell->GetColIndex(colIndex); + // We're checking mNumCols instead of mCols.Length() here because mCols can + // be empty even if mNumCols > 0. + NS_ASSERTION(size_t(colIndex) < mNumCols, "out-of-bounds column index"); + if (size_t(colIndex) >= mNumCols) { + return DrawResult::SUCCESS; + } + + // If callers call PaintRowGroup or PaintRow directly, we haven't processed + // our columns. Ignore column / col group backgrounds in that case. + bool haveColumns = !mCols.IsEmpty(); + + DrawResult result = DrawResult::SUCCESS; + + //Paint column group background + if (haveColumns && mCols[colIndex].mColGroup.IsVisible()) { + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, + mDirtyRect, + mCols[colIndex].mColGroup.mRect + mRenderPt, + mCols[colIndex].mColGroup.mFrame, + mBGPaintFlags); + params.bgClipRect = &aColBGRect; + result &= + nsCSSRendering::PaintStyleImageLayerWithSC(params, mRenderingContext, + mCols[colIndex].mColGroup.mFrame->StyleContext(), + mCols[colIndex].mColGroup.StyleBorder(mZeroBorder)); + } + + //Paint column background + if (haveColumns && mCols[colIndex].mCol.IsVisible()) { + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, + mDirtyRect, + mCols[colIndex].mCol.mRect + mRenderPt, + mCols[colIndex].mCol.mFrame, + mBGPaintFlags); + params.bgClipRect = &aColBGRect; + result &= + nsCSSRendering::PaintStyleImageLayerWithSC(params, mRenderingContext, + mCols[colIndex].mCol.mFrame->StyleContext(), + mCols[colIndex].mCol.StyleBorder(mZeroBorder)); + } + + //Paint row group background + if (aRowGroupBGData.IsVisible()) { + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, + mDirtyRect, + aRowGroupBGData.mRect + mRenderPt, + aRowGroupBGData.mFrame, mBGPaintFlags); + params.bgClipRect = &aRowGroupBGRect; + result &= + nsCSSRendering::PaintStyleImageLayerWithSC(params, mRenderingContext, + aRowGroupBGData.mFrame->StyleContext(), + aRowGroupBGData.StyleBorder(mZeroBorder)); + } + + //Paint row background + if (aRowBGData.IsVisible()) { + nsCSSRendering::PaintBGParams params = + nsCSSRendering::PaintBGParams::ForAllLayers(*mPresContext, + mDirtyRect, + aRowBGData.mRect + mRenderPt, + aRowBGData.mFrame, mBGPaintFlags); + params.bgClipRect = &aRowBGRect; + result &= + nsCSSRendering::PaintStyleImageLayerWithSC(params, mRenderingContext, + aRowBGData.mFrame->StyleContext(), + aRowBGData.StyleBorder(mZeroBorder)); + } + + //Paint cell background in border-collapse unless we're just passing + if (mIsBorderCollapse && !aPassSelf) { + result &= + aCell->PaintCellBackground(mRenderingContext, mDirtyRect, + aCellBGRect.TopLeft(), mBGPaintFlags); + } + + return result; +} + +void +TableBackgroundPainter::ComputeCellBackgrounds(nsTableCellFrame* aCell, + const TableBackgroundData& aRowGroupBGData, + const TableBackgroundData& aRowBGData, + nsRect& aCellBGRect, + nsRect& aRowBGRect, + nsRect& aRowGroupBGRect, + nsRect& aColBGRect) +{ + // We need to compute table background layer rects for this cell space, + // adjusted for possible relative positioning. This behavior is not specified + // at the time of this writing, but the approach below should be web + // compatible. + // + // Our goal is that relative positioning of a table part should leave + // backgrounds *under* that part unchanged. ("Under" being defined by CSS 2.1 + // Section 17.5.1.) If a cell is positioned, we do not expect the row + // background to move. On the other hand, the backgrounds of layers *above* + // the positioned part are taken along for the ride -- for example, + // positioning a row group will also cause the row background to be drawn in + // the new location, unless it has further positioning applied. + // + // Each table part layer has its position stored in the coordinate space of + // the layer below (which is to say, its geometric parent), and the stored + // position is the post-relative-positioning one. The position of each + // background layer rect is thus determined by peeling off successive table + // part layers, removing the contribution of each layer's positioning one by + // one. Every rect we generate will be the same size, the size of the cell + // space. + + // We cannot rely on the row group background data to be available, since some + // callers enter through PaintRow. + nsIFrame* rowGroupFrame = + aRowGroupBGData.mFrame ? aRowGroupBGData.mFrame : aRowBGData.mFrame->GetParent(); + + // The cell background goes at the cell's position, translated to use the same + // coordinate system as aRowBGData. + aCellBGRect = aCell->GetRect() + aRowBGData.mRect.TopLeft() + mRenderPt; + + // The row background goes at the normal position of the cell, which is to say + // the position without relative positioning applied. + aRowBGRect = aCellBGRect + (aCell->GetNormalPosition() - aCell->GetPosition()); + + // The row group background goes at the position we'd find the cell if neither + // the cell's relative positioning nor the row's were applied. + aRowGroupBGRect = aRowBGRect + + (aRowBGData.mFrame->GetNormalPosition() - aRowBGData.mFrame->GetPosition()); + + // The column and column group backgrounds (they're always at the same + // location, since relative positioning doesn't apply to columns or column + // groups) are drawn at the position we'd find the cell if none of the cell's, + // row's, or row group's relative positioning were applied. + aColBGRect = aRowGroupBGRect + + (rowGroupFrame->GetNormalPosition() - rowGroupFrame->GetPosition()); + +} diff --git a/layout/tables/nsTablePainter.h b/layout/tables/nsTablePainter.h new file mode 100644 index 0000000000000..dfba42156507e --- /dev/null +++ b/layout/tables/nsTablePainter.h @@ -0,0 +1,268 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsTablePainter_h__ +#define nsTablePainter_h__ + +#include "imgIContainer.h" + +#include "celldata.h" + +// flags for Paint, PaintChild, PaintChildren are currently only used by tables. +//Table-based paint call; not a direct call as with views +#define NS_PAINT_FLAG_TABLE_BG_PAINT 0x00000001 +//Cells should paint their backgrounds only, no children +#define NS_PAINT_FLAG_TABLE_CELL_BG_PASS 0x00000002 + +class nsIFrame; +class nsTableFrame; +class nsTableRowGroupFrame; +class nsTableRowFrame; +class nsTableCellFrame; + +class TableBackgroundPainter +{ + /* + * Helper class for painting table backgrounds + * + */ + + typedef mozilla::image::DrawResult DrawResult; + + public: + + enum Origin { eOrigin_Table, eOrigin_TableRowGroup, eOrigin_TableRow }; + + /** Public constructor + * @param aTableFrame - the table's table frame + * @param aOrigin - what type of table frame is creating this instance + * @param aPresContext - the presentation context + * @param aRenderingContext - the rendering context + * @param aDirtyRect - the area that needs to be painted, + * relative to aRenderingContext + * @param aPt - offset of the table frame relative to + * aRenderingContext + * @param aBGPaintFlags - Flags of the nsCSSRendering::PAINTBG_* variety + */ + TableBackgroundPainter(nsTableFrame* aTableFrame, + Origin aOrigin, + nsPresContext* aPresContext, + nsRenderingContext& aRenderingContext, + const nsRect& aDirtyRect, + const nsPoint& aPt, + uint32_t aBGPaintFlags); + + /** Destructor */ + ~TableBackgroundPainter(); + + /* ~*~ The Border Collapse Painting Issue ~*~ + + In border-collapse, the *table* paints the cells' borders, + so we need to make sure the backgrounds get painted first + (underneath) by doing a cell-background-only painting pass. + */ + + /* ~*~ Using nsTablePainter Background Painting ~*~ + + A call to PaintTable will normally paint all of the table's + elements (except for the table background, if aPaintTableBackground + is false). + Elements with views however, will be skipped and must create their + own painter to call the appropriate paint function in their ::Paint + method (e.g. painter.PaintRow in nsTableRow::Paint) + */ + + /** Paint background for the table frame (if requested) and its children + * down through cells. + * (Cells themselves will only be painted in border collapse) + * Table must do a flagged TABLE_BG_PAINT ::Paint call on its + * children afterwards + * @param aTableFrame - the table frame + * @param aDeflate - deflation needed to bring table's mRect + * to the outer grid lines in border-collapse + * @param aPaintTableBackground - if true, the table background + * is included, otherwise it isn't + * @returns DrawResult::SUCCESS if all painting was successful. If some + * painting failed or an improved result could be achieved by sync + * decoding images, returns another value. + */ + DrawResult PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate, + bool aPaintTableBackground); + + /** Paint background for the row group and its children down through cells + * (Cells themselves will only be painted in border collapse) + * Standards mode only + * Table Row Group must do a flagged TABLE_BG_PAINT ::Paint call on its + * children afterwards + * @param aFrame - the table row group frame + * @returns DrawResult::SUCCESS if all painting was successful. If some + * painting failed or an improved result could be achieved by sync + * decoding images, returns another value. + */ + DrawResult PaintRowGroup(nsTableRowGroupFrame* aFrame); + + /** Paint background for the row and its children down through cells + * (Cells themselves will only be painted in border collapse) + * Standards mode only + * Table Row must do a flagged TABLE_BG_PAINT ::Paint call on its + * children afterwards + * @param aFrame - the table row frame + * @returns DrawResult::SUCCESS if all painting was successful. If some + * painting failed or an improved result could be achieved by sync + * decoding images, returns another value. + */ + DrawResult PaintRow(nsTableRowFrame* aFrame); + + private: + struct TableBackgroundData; + + /** Paint table frame's background + * @param aTableFrame - the table frame + * @param aFirstRowGroup - the first (in layout order) row group + * may be null + * @param aLastRowGroup - the last (in layout order) row group + * may be null + * @param aDeflate - adjustment to frame's rect (used for quirks BC) + * may be null + */ + DrawResult PaintTableFrame(nsTableFrame* aTableFrame, + nsTableRowGroupFrame* aFirstRowGroup, + nsTableRowGroupFrame* aLastRowGroup, + const nsMargin& aDeflate); + + /* aPassThrough params indicate whether to paint the element or to just + * pass through and paint underlying layers only. + * aRowGroupBGData is not a const reference because the function modifies + * its copy. Same for aRowBGData in PaintRow. + * See Public versions for function descriptions + */ + DrawResult PaintRowGroup(nsTableRowGroupFrame* aFrame, + TableBackgroundData aRowGroupBGData, + bool aPassThrough); + + DrawResult PaintRow(nsTableRowFrame* aFrame, + const TableBackgroundData& aRowGroupBGData, + TableBackgroundData aRowBGData, + bool aPassThrough); + + /** Paint table background layers for this cell space + * Also paints cell's own background in border-collapse mode + * @param aCell - the cell + * @param aRowGroupBGData - background drawing info for the row group + * @param aRowBGData - background drawing info for the row + * @param aCellBGRect - background rect for the cell + * @param aRowBGRect - background rect for the row + * @param aRowGroupBGRect - background rect for the row group + * @param aColBGRect - background rect for the column and column group + * @param aPassSelf - pass this cell; i.e. paint only underlying layers + */ + DrawResult PaintCell(nsTableCellFrame* aCell, + const TableBackgroundData& aRowGroupBGData, + const TableBackgroundData& aRowBGData, + nsRect& aCellBGRect, + nsRect& aRowBGRect, + nsRect& aRowGroupBGRect, + nsRect& aColBGRect, + bool aPassSelf); + + /** Compute table background layer positions for this cell space + * @param aCell - the cell + * @param aRowGroupBGData - background drawing info for the row group + * @param aRowBGData - background drawing info for the row + * @param aCellBGRectOut - outparam: background rect for the cell + * @param aRowBGRectOut - outparam: background rect for the row + * @param aRowGroupBGRectOut - outparam: background rect for the row group + * @param aColBGRectOut - outparam: background rect for the column + and column group + */ + void ComputeCellBackgrounds(nsTableCellFrame* aCell, + const TableBackgroundData& aRowGroupBGData, + const TableBackgroundData& aRowBGData, + nsRect& aCellBGRect, + nsRect& aRowBGRect, + nsRect& aRowGroupBGRect, + nsRect& aColBGRect); + + /** Translate mRenderingContext, mDirtyRect, and mCols' column and + * colgroup coords + * @param aDX - origin's x-coord change + * @param aDY - origin's y-coord change + */ + void TranslateContext(nscoord aDX, + nscoord aDY); + + struct TableBackgroundData { + public: + /** + * Construct an empty TableBackgroundData instance, which is invisible. + */ + TableBackgroundData(); + + /** + * Construct a TableBackgroundData instance for a frame. Visibility will + * be derived from the frame and can be overridden using MakeInvisible(). + */ + explicit TableBackgroundData(nsIFrame* aFrame); + + /** Destructor */ + ~TableBackgroundData() {} + + /** Data is valid & frame is visible */ + bool IsVisible() const { return mVisible; } + + /** Override visibility of the frame, force it to be invisible */ + void MakeInvisible() { mVisible = false; } + + /** True if need to set border-collapse border; must call SetFull beforehand */ + bool ShouldSetBCBorder() const; + + /** Set border-collapse border with aBorderWidth as widths */ + void SetBCBorder(const nsMargin& aBorderWidth); + + /** + * @param aZeroBorder An nsStyleBorder instance that has been initialized + * for the right nsPresContext, with all border widths + * set to zero and border styles set to solid. + * @return The nsStyleBorder that should be used for rendering + * this background. + */ + nsStyleBorder StyleBorder(const nsStyleBorder& aZeroBorder) const; + + nsIFrame* const mFrame; + + /** mRect is the rect of mFrame in the current coordinate system */ + nsRect mRect; + + private: + nsMargin mSynthBorderWidths; + bool mVisible; + bool mUsesSynthBorder; + }; + + struct ColData { + ColData(nsIFrame* aFrame, TableBackgroundData& aColGroupBGData); + TableBackgroundData mCol; + TableBackgroundData& mColGroup; // reference to col's parent colgroup's data, owned by TablePainter in mColGroups + }; + + nsPresContext* mPresContext; + nsRenderingContext& mRenderingContext; + nsPoint mRenderPt; + nsRect mDirtyRect; +#ifdef DEBUG + nsCompatibility mCompatMode; +#endif + bool mIsBorderCollapse; + Origin mOrigin; //user's table frame type + + nsTArray mColGroups; + nsTArray mCols; + size_t mNumCols; + + nsStyleBorder mZeroBorder; //cached zero-width border + uint32_t mBGPaintFlags; +}; + +#endif diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index 9dead7481d1bc..509c572e23ee8 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -577,6 +577,45 @@ nsTableRowFrame::CalcBSize(const ReflowInput& aReflowInput) return GetInitialBSize(); } +/** + * We need a custom display item for table row backgrounds. This is only used + * when the table row is the root of a stacking context (e.g., has 'opacity'). + * Table row backgrounds can extend beyond the row frame bounds, when + * the row contains row-spanning cells. + */ +class nsDisplayTableRowBackground : public nsDisplayTableItem { +public: + nsDisplayTableRowBackground(nsDisplayListBuilder* aBuilder, + nsTableRowFrame* aFrame) : + nsDisplayTableItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayTableRowBackground); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayTableRowBackground() { + MOZ_COUNT_DTOR(nsDisplayTableRowBackground); + } +#endif + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) override; + NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND) +}; + +void +nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + auto rowFrame = static_cast(mFrame); + TableBackgroundPainter painter(rowFrame->GetTableFrame(), + TableBackgroundPainter::eOrigin_TableRow, + mFrame->PresContext(), *aCtx, + mVisibleRect, ToReferenceFrame(), + aBuilder->GetBackgroundPaintFlags()); + + DrawResult result = painter.PaintRow(rowFrame); + nsDisplayTableItemGeometry::UpdateDrawResult(this, result); +} + void nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, diff --git a/layout/tables/nsTableRowFrame.h b/layout/tables/nsTableRowFrame.h index 6259271ffb05c..63f27f2d50162 100644 --- a/layout/tables/nsTableRowFrame.h +++ b/layout/tables/nsTableRowFrame.h @@ -8,6 +8,7 @@ #include "mozilla/Attributes.h" #include "nscore.h" #include "nsContainerFrame.h" +#include "nsTablePainter.h" #include "nsTableRowGroupFrame.h" #include "mozilla/WritingModes.h" diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index b1482da82c888..9d0b575c59db3 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -198,6 +198,46 @@ nsTableRowGroupFrame::InitRepeatedFrame(nsTableRowGroupFrame* aHeaderFooterFrame return NS_OK; } +/** + * We need a custom display item for table row backgrounds. This is only used + * when the table row is the root of a stacking context (e.g., has 'opacity'). + * Table row backgrounds can extend beyond the row frame bounds, when + * the row contains row-spanning cells. + */ +class nsDisplayTableRowGroupBackground : public nsDisplayTableItem { +public: + nsDisplayTableRowGroupBackground(nsDisplayListBuilder* aBuilder, + nsTableRowGroupFrame* aFrame) : + nsDisplayTableItem(aBuilder, aFrame) { + MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground); + } +#ifdef NS_BUILD_REFCNT_LOGGING + virtual ~nsDisplayTableRowGroupBackground() { + MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground); + } +#endif + + virtual void Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) override; + + NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND) +}; + +void +nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder, + nsRenderingContext* aCtx) +{ + auto rgFrame = static_cast(mFrame); + TableBackgroundPainter painter(rgFrame->GetTableFrame(), + TableBackgroundPainter::eOrigin_TableRowGroup, + mFrame->PresContext(), *aCtx, + mVisibleRect, ToReferenceFrame(), + aBuilder->GetBackgroundPaintFlags()); + + DrawResult result = painter.PaintRowGroup(rgFrame); + nsDisplayTableItemGeometry::UpdateDrawResult(this, result); +} + // Handle the child-traversal part of DisplayGenericTablePart static void DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, diff --git a/layout/tables/nsTableRowGroupFrame.h b/layout/tables/nsTableRowGroupFrame.h index 66098d2ba0242..477c716292900 100644 --- a/layout/tables/nsTableRowGroupFrame.h +++ b/layout/tables/nsTableRowGroupFrame.h @@ -10,6 +10,7 @@ #include "nsContainerFrame.h" #include "nsIAtom.h" #include "nsILineIterator.h" +#include "nsTablePainter.h" #include "nsTArray.h" #include "nsTableFrame.h" #include "mozilla/WritingModes.h" From fb1c5543f991a0f652412e6c8af665a8ea8c7a38 Mon Sep 17 00:00:00 2001 From: "Carsten \"Tomcat\" Book" Date: Thu, 4 May 2017 17:00:20 +0200 Subject: [PATCH 062/131] Backed out changeset 93acd240b578 (bug 929484) for reftest failures in border-separate-opacity-table-column-group.html --- layout/painting/nsDisplayItemTypesList.h | 1 - layout/painting/nsDisplayList.cpp | 9 +- layout/painting/nsDisplayList.h | 3 +- layout/tables/nsTableCellFrame.cpp | 104 +++++---- layout/tables/nsTableColFrame.cpp | 8 - layout/tables/nsTableColFrame.h | 5 +- layout/tables/nsTableColGroupFrame.cpp | 8 - layout/tables/nsTableColGroupFrame.h | 5 +- layout/tables/nsTableFrame.cpp | 276 +++++++++++------------ layout/tables/nsTableFrame.h | 1 + layout/tables/nsTableRowFrame.cpp | 16 +- layout/tables/nsTableRowGroupFrame.cpp | 13 +- layout/tables/nsTableWrapperFrame.cpp | 6 +- 13 files changed, 228 insertions(+), 227 deletions(-) diff --git a/layout/painting/nsDisplayItemTypesList.h b/layout/painting/nsDisplayItemTypesList.h index be3950e4d8f4e..3bb677e314699 100644 --- a/layout/painting/nsDisplayItemTypesList.h +++ b/layout/painting/nsDisplayItemTypesList.h @@ -59,7 +59,6 @@ DECLARE_DISPLAY_ITEM_TYPE(TABLE_CELL_SELECTION) DECLARE_DISPLAY_ITEM_TYPE(TABLE_ROW_BACKGROUND) DECLARE_DISPLAY_ITEM_TYPE(TABLE_ROW_GROUP_BACKGROUND) DECLARE_DISPLAY_ITEM_TYPE(TABLE_BORDER_BACKGROUND) -DECLARE_DISPLAY_ITEM_TYPE(TABLE_BORDER_COLLAPSE) DECLARE_DISPLAY_ITEM_TYPE(TEXT) DECLARE_DISPLAY_ITEM_TYPE(TEXT_OVERFLOW) DECLARE_DISPLAY_ITEM_TYPE_FLAGS(TRANSFORM,TYPE_RENDERS_NO_IMAGES) diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp index 3789074d9b209..89c12f8b1ced2 100644 --- a/layout/painting/nsDisplayList.cpp +++ b/layout/painting/nsDisplayList.cpp @@ -3062,16 +3062,11 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil const nsRect& aBackgroundRect, nsDisplayList* aList, bool aAllowWillPaintBorderOptimization, - nsStyleContext* aStyleContext, - const nsRect& aBackgroundOriginRect) + nsStyleContext* aStyleContext) { nsStyleContext* bgSC = aStyleContext; const nsStyleBackground* bg = nullptr; nsRect bgRect = aBackgroundRect + aBuilder->ToReferenceFrame(aFrame); - nsRect bgOriginRect = bgRect; - if (!aBackgroundOriginRect.IsEmpty()) { - bgOriginRect = aBackgroundOriginRect + aBuilder->ToReferenceFrame(aFrame); - } nsPresContext* presContext = aFrame->PresContext(); bool isThemed = aFrame->IsThemed(); if (!isThemed) { @@ -3180,7 +3175,7 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil nsDisplayList thisItemList; nsDisplayBackgroundImage::InitData bgData = - nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgOriginRect, bg, + nsDisplayBackgroundImage::GetInitData(aBuilder, aFrame, i, bgRect, bg, LayerizeFixed::DO_NOT_LAYERIZE_FIXED_BACKGROUND_IF_AVOIDING_COMPONENT_ALPHA_LAYERS); if (bgData.shouldFixToViewport) { diff --git a/layout/painting/nsDisplayList.h b/layout/painting/nsDisplayList.h index e2f441ded8d0e..c1227cc3a39a6 100644 --- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -3071,8 +3071,7 @@ class nsDisplayBackgroundImage : public nsDisplayImageContainer { const nsRect& aBackgroundRect, nsDisplayList* aList, bool aAllowWillPaintBorderOptimization = true, - nsStyleContext* aStyleContext = nullptr, - const nsRect& aBackgroundOriginRect = nsRect()); + nsStyleContext* aStyleContext = nullptr); virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager, diff --git a/layout/tables/nsTableCellFrame.cpp b/layout/tables/nsTableCellFrame.cpp index 09090847bed89..9ff1180a1739d 100644 --- a/layout/tables/nsTableCellFrame.cpp +++ b/layout/tables/nsTableCellFrame.cpp @@ -490,51 +490,71 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) { DO_GLOBAL_REFLOW_COUNT_DSP("nsTableCellFrame"); - nsTableFrame* tableFrame = GetTableFrame(); - int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ? - StyleTableBorder()->mEmptyCells - : NS_STYLE_TABLE_EMPTY_CELLS_SHOW; - // take account of 'empty-cells' - if (StyleVisibility()->IsVisible() && - (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) { - // display outset box-shadows if we need to. - bool hasBoxShadow = !!StyleEffects()->mBoxShadow; - if (hasBoxShadow) { - aLists.BorderBackground()->AppendNewToTop( - new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); - } - - // display background if we need to. - if (aBuilder->IsForEventDelivery() || - !StyleBackground()->IsTransparent(this) || - StyleDisplay()->UsedAppearance()) { - nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, - this, - GetRectRelativeToSelf(), - aLists.BorderBackground()); - } - - // display inset box-shadows if we need to. - if (hasBoxShadow) { - aLists.BorderBackground()->AppendNewToTop( - new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this)); - } - - // display borders if we need to - ProcessBorders(tableFrame, aBuilder, aLists); - - // and display the selection border if we need to - if (IsSelected()) { - aLists.BorderBackground()->AppendNewToTop(new (aBuilder) - nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection, - "TableCellSelection", - nsDisplayItem::TYPE_TABLE_CELL_SELECTION)); + if (IsVisibleInSelection(aBuilder)) { + nsTableFrame* tableFrame = GetTableFrame(); + int32_t emptyCellStyle = GetContentEmpty() && !tableFrame->IsBorderCollapse() ? + StyleTableBorder()->mEmptyCells + : NS_STYLE_TABLE_EMPTY_CELLS_SHOW; + // take account of 'empty-cells' + if (StyleVisibility()->IsVisible() && + (NS_STYLE_TABLE_EMPTY_CELLS_HIDE != emptyCellStyle)) { + // display outset box-shadows if we need to. + bool hasBoxShadow = !!StyleEffects()->mBoxShadow; + if (hasBoxShadow) { + aLists.BorderBackground()->AppendNewToTop( + new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this)); + } + + // display background if we need to. + if (aBuilder->IsForEventDelivery() || + !StyleBackground()->IsTransparent(this) || + StyleDisplay()->UsedAppearance()) { + if (!tableFrame->IsBorderCollapse()) { + nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, + this, + GetRectRelativeToSelf(), + aLists.BorderBackground()); + } else if (aBuilder->IsAtRootOfPseudoStackingContext() || + aBuilder->IsForEventDelivery()) { + // The cell background was not painted by the nsTablePainter, + // so we need to do it. We have special background processing here + // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline + nsDisplayTableItem* item = + new (aBuilder) nsDisplayTableCellBackground(aBuilder, this); + aLists.BorderBackground()->AppendNewToTop(item); + item->UpdateForFrameBackground(this); + } else { + // The nsTablePainter will paint our background. Make sure it + // knows if we're background-attachment:fixed. + nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); + if (currentItem) { + currentItem->UpdateForFrameBackground(this); + } + } + } + + // display inset box-shadows if we need to. + if (hasBoxShadow) { + aLists.BorderBackground()->AppendNewToTop( + new (aBuilder) nsDisplayBoxShadowInner(aBuilder, this)); + } + + // display borders if we need to + ProcessBorders(tableFrame, aBuilder, aLists); + + // and display the selection border if we need to + if (IsSelected()) { + aLists.BorderBackground()->AppendNewToTop(new (aBuilder) + nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection, + "TableCellSelection", + nsDisplayItem::TYPE_TABLE_CELL_SELECTION)); + } } + + // the 'empty-cells' property has no effect on 'outline' + DisplayOutline(aBuilder, aLists); } - // the 'empty-cells' property has no effect on 'outline' - DisplayOutline(aBuilder, aLists); - // Push a null 'current table item' so that descendant tables can't // accidentally mess with our table nsAutoPushCurrentTableItem pushTableItem; diff --git a/layout/tables/nsTableColFrame.cpp b/layout/tables/nsTableColFrame.cpp index be401bedd09cc..aa74b312ad27c 100644 --- a/layout/tables/nsTableColFrame.cpp +++ b/layout/tables/nsTableColFrame.cpp @@ -122,14 +122,6 @@ nsTableColFrame::Reflow(nsPresContext* aPresContext, NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); } -void -nsTableColFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, - const nsDisplayListSet& aLists) -{ - nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists); -} - int32_t nsTableColFrame::GetSpan() { return StyleTable()->mSpan; diff --git a/layout/tables/nsTableColFrame.h b/layout/tables/nsTableColFrame.h index eef4e6c8c589c..6da2c777885e7 100644 --- a/layout/tables/nsTableColFrame.h +++ b/layout/tables/nsTableColFrame.h @@ -53,9 +53,12 @@ class nsTableColFrame final : public nsSplittableFrame const ReflowInput& aReflowInput, nsReflowStatus& aStatus) override; + /** + * Table columns never paint anything, nor receive events. + */ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, - const nsDisplayListSet& aLists) override; + const nsDisplayListSet& aLists) override {} #ifdef DEBUG_FRAME_DUMP virtual nsresult GetFrameName(nsAString& aResult) const override; diff --git a/layout/tables/nsTableColGroupFrame.cpp b/layout/tables/nsTableColGroupFrame.cpp index c6edd525bf084..4fddf6ce3e538 100644 --- a/layout/tables/nsTableColGroupFrame.cpp +++ b/layout/tables/nsTableColGroupFrame.cpp @@ -386,14 +386,6 @@ nsTableColGroupFrame::Reflow(nsPresContext* aPresContext, NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize); } -void -nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - const nsRect& aDirtyRect, - const nsDisplayListSet& aLists) -{ - nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists); -} - nsTableColFrame * nsTableColGroupFrame::GetFirstColumn() { return GetNextColumn(nullptr); diff --git a/layout/tables/nsTableColGroupFrame.h b/layout/tables/nsTableColGroupFrame.h index 50681e8edc0ea..21a876f5e9add 100644 --- a/layout/tables/nsTableColGroupFrame.h +++ b/layout/tables/nsTableColGroupFrame.h @@ -50,9 +50,12 @@ class nsTableColGroupFrame final : public nsContainerFrame return static_cast(parent); } + /** + * ColGroups never paint anything, nor receive events. + */ virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, - const nsDisplayListSet& aLists) override; + const nsDisplayListSet& aLists) override {} /** A colgroup can be caused by three things: * 1) An element with table-column-group display diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index 1fc52c66e92d8..2793535523e7f 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -1238,46 +1238,6 @@ nsDisplayTableItem::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); } -// A display item that draws all collapsed borders for a table. -// At some point, we may want to find a nicer partitioning for dividing -// border-collapse segments into their own display items. -class nsDisplayTableBorderCollapse : public nsDisplayTableItem { -public: - nsDisplayTableBorderCollapse(nsDisplayListBuilder* aBuilder, - nsTableFrame* aFrame) - : nsDisplayTableItem(aBuilder, aFrame) { - MOZ_COUNT_CTOR(nsDisplayTableBorderCollapse); - } -#ifdef NS_BUILD_REFCNT_LOGGING - virtual ~nsDisplayTableBorderCollapse() { - MOZ_COUNT_DTOR(nsDisplayTableBorderCollapse); - } -#endif - - virtual void Paint(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx) override; - NS_DISPLAY_DECL_NAME("TableBorderCollapse", TYPE_TABLE_BORDER_COLLAPSE) -}; - -void -nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder, - nsRenderingContext* aCtx) -{ - nsPoint pt = ToReferenceFrame(); - DrawTarget* drawTarget = aCtx->GetDrawTarget(); - - gfxPoint devPixelOffset = - nsLayoutUtils::PointToGfxPoint(pt, mFrame->PresContext()->AppUnitsPerDevPixel()); - - // XXX we should probably get rid of this translation at some stage - // But that would mean modifying PaintBCBorders, ugh - AutoRestoreTransform autoRestoreTransform(drawTarget); - drawTarget->SetTransform( - drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset))); - - static_cast(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt); -} - class nsDisplayTableBorderBackground : public nsDisplayTableItem { public: nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, @@ -1297,6 +1257,20 @@ class nsDisplayTableBorderBackground : public nsDisplayTableItem { NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND) }; +#ifdef DEBUG +static bool +IsFrameAllowedInTable(LayoutFrameType aType) +{ + return IS_TABLE_CELL(aType) || + LayoutFrameType::TableRow == aType || + LayoutFrameType::TableRowGroup == aType || + LayoutFrameType::Scroll == aType || + LayoutFrameType::Table == aType || + LayoutFrameType::TableCol == aType || + LayoutFrameType::TableColGroup == aType; +} +#endif + void nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) @@ -1308,6 +1282,25 @@ nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, nsDisplayTableItemGeometry::UpdateDrawResult(this, result); } +static int32_t +GetTablePartRank(nsDisplayItem* aItem) +{ + LayoutFrameType type = aItem->Frame()->Type(); + if (type == LayoutFrameType::Table) + return 0; + if (type == LayoutFrameType::TableRowGroup) + return 1; + if (type == LayoutFrameType::TableRow) + return 2; + return 3; +} + +struct TablePartRankComparator { + bool operator()(nsDisplayItem* aItem1, nsDisplayItem* aItem2) const { + return GetTablePartRank(aItem1) < GetTablePartRank(aItem2); + } +}; + /* static */ void nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) @@ -1320,73 +1313,31 @@ nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, // stacking context, in which case the child won't use its passed-in // BorderBackground list anyway. It does affect cell borders though; this // lets us get cell borders into the nsTableFrame's BorderBackground list. - for (nsIFrame* kid : aFrame->GetChildList(kColGroupList)) { - aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); - } - for (nsIFrame* kid : aFrame->PrincipalChildList()) { aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); } } -static void -PaintRowBackground(nsTableRowFrame* aRow, - nsIFrame* aFrame, - nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists, - const nsPoint& aOffset = nsPoint()) -{ - // Compute background rect by iterating all cell frame. - for (nsTableCellFrame* cell = aRow->GetFirstCell(); cell; cell = cell->GetNextCell()) { - auto cellRect = cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + aOffset; - nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect, - aLists.BorderBackground(), - true, nullptr, - aFrame->GetRectRelativeToSelf()); - } -} - -static void -PaintRowGroupBackground(nsTableRowGroupFrame* aRowGroup, - nsIFrame* aFrame, - nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists) -{ - for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) { - PaintRowBackground(row, aFrame, aBuilder, aLists, row->GetNormalPosition()); - } -} - -static void -PaintRowGroupBackgroundByColIdx(nsTableRowGroupFrame* aRowGroup, - nsIFrame* aFrame, - nsDisplayListBuilder* aBuilder, - const nsDisplayListSet& aLists, - const nsTArray& aColIdx, - const nsPoint& aOffset) -{ - for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) { - for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) { - int32_t curColIdx; - cell->GetColIndex(curColIdx); - if (aColIdx.Contains(curColIdx)) { - auto cellRect = cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + row->GetNormalPosition() + aOffset; - nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect, - aLists.BorderBackground(), - true, nullptr, - aFrame->GetRectRelativeToSelf()); - } - } - } -} - /* static */ void nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists, + nsDisplayTableItem* aDisplayItem, DisplayGenericTablePartTraversal aTraversal) { + nsDisplayList eventsBorderBackground; + // If we need to sort the event backgrounds, then we'll put descendants' + // display items into their own set of lists. + bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery(); + nsDisplayListCollection separatedCollection; + const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists; + + nsAutoPushCurrentTableItem pushTableItem; + if (aDisplayItem) { + pushTableItem.Push(aBuilder, aDisplayItem); + } + if (aFrame->IsVisibleForPainting(aBuilder)) { nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); // currentItem may be null, when none of the table parts have a @@ -1398,74 +1349,34 @@ nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, // Paint the outset box-shadows for the table frames bool hasBoxShadow = aFrame->StyleEffects()->mBoxShadow != nullptr; if (hasBoxShadow) { - aLists.BorderBackground()->AppendNewToTop( + lists->BorderBackground()->AppendNewToTop( new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame)); } - if (aFrame->IsTableRowGroupFrame()) { - nsTableRowGroupFrame* rowGroup = static_cast(aFrame); - PaintRowGroupBackground(rowGroup, aFrame, aBuilder, aLists); - } else if (aFrame->IsTableRowFrame()) { - nsTableRowFrame* row = static_cast(aFrame); - PaintRowBackground(row, aFrame, aBuilder, aLists); - } else if (aFrame->IsTableColGroupFrame()) { - // Compute background rect by iterating all cell frame. - nsTableColGroupFrame* colGroup = static_cast(aFrame); - // Collecting column index. - AutoTArray colIdx; - for (nsTableColFrame* col = colGroup->GetFirstColumn(); col; col = col->GetNextCol()) { - colIdx.AppendElement(col->GetColIndex()); - } - - nsTableFrame* table = colGroup->GetTableFrame(); - RowGroupArray rowGroups; - table->OrderRowGroups(rowGroups); - for (nsTableRowGroupFrame* rowGroup : rowGroups) { - auto offset = rowGroup->GetNormalPosition() - colGroup->GetNormalPosition(); - PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, colIdx, offset); - } - } else if (aFrame->IsTableColFrame()) { - // Compute background rect by iterating all cell frame. - nsTableColFrame* col = static_cast(aFrame); - AutoTArray colIdx; - colIdx.AppendElement(col->GetColIndex()); - - nsTableFrame* table = col->GetTableFrame(); - RowGroupArray rowGroups; - table->OrderRowGroups(rowGroups); - for (nsTableRowGroupFrame* rowGroup : rowGroups) { - auto offset = rowGroup->GetNormalPosition() - - col->GetNormalPosition() - - col->GetTableColGroupFrame()->GetNormalPosition(); - PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, colIdx, offset); - } - } else { + // Create dedicated background display items per-frame when we're + // handling events. + // XXX how to handle collapsed borders? + if (aBuilder->IsForEventDelivery()) { nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, aFrame->GetRectRelativeToSelf(), - aLists.BorderBackground()); + lists->BorderBackground()); } // Paint the inset box-shadows for the table frames if (hasBoxShadow) { - aLists.BorderBackground()->AppendNewToTop( + lists->BorderBackground()->AppendNewToTop( new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame)); } } - aTraversal(aBuilder, aFrame, aDirtyRect, aLists); + aTraversal(aBuilder, aFrame, aDirtyRect, *lists); - if (aFrame->IsVisibleForPainting(aBuilder)) { - if (aFrame->IsTableFrame()) { - nsTableFrame* table = static_cast(aFrame); - // In the collapsed border model, overlay all collapsed borders. - if (table->IsBorderCollapse()) { - aLists.BorderBackground()->AppendNewToTop( - new (aBuilder) nsDisplayTableBorderCollapse(aBuilder, table)); - } else { - aLists.BorderBackground()->AppendNewToTop( - new (aBuilder) nsDisplayBorder(aBuilder, table)); - } - } + if (sortEventBackgrounds) { + // Ensure that the table frame event background goes before the + // table rowgroups event backgrounds, before the table row event backgrounds, + // before everything else (cells and their blocks) + separatedCollection.BorderBackground()->Sort(TablePartRankComparator()); + separatedCollection.MoveTo(aLists); } aFrame->DisplayOutline(aBuilder, aLists); @@ -1493,6 +1404,43 @@ static inline bool FrameHasBorderOrBackground(nsTableFrame* tableFrame, nsIFrame return false; } +static bool +AnyTablePartHasBorderOrBackground(nsTableFrame* aTableFrame, + nsIFrame* aStart, + nsIFrame* aEnd) +{ + for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { + NS_ASSERTION(IsFrameAllowedInTable(f->Type()), "unexpected frame type"); + + if (FrameHasBorderOrBackground(aTableFrame, f)) + return true; + + nsTableCellFrame *cellFrame = do_QueryFrame(f); + if (cellFrame) + continue; + + if (AnyTablePartHasBorderOrBackground(aTableFrame, + f->PrincipalChildList().FirstChild(), + nullptr)) + return true; + } + + return false; +} + +static void +UpdateItemForColGroupBackgrounds(nsDisplayTableItem* item, + const nsFrameList& aFrames) { + for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) { + nsTableColGroupFrame* cg = static_cast(e.get()); + item->UpdateForFrameBackground(cg); + for (nsTableColFrame* colFrame = cg->GetFirstColumn(); colFrame; + colFrame = colFrame->GetNextCol()) { + item->UpdateForFrameBackground(colFrame); + } + } +} + // table paint code is concerned primarily with borders and bg color // SEC: TODO: adjust the rect for captions void @@ -1502,7 +1450,35 @@ nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, { DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255)); - DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists); + nsDisplayTableItem* item = nullptr; + if (IsVisibleInSelection(aBuilder)) { + nsMargin deflate = GetDeflationForBackground(PresContext()); + if (StyleVisibility()->IsVisible()) { + // If 'deflate' is (0,0,0,0) then we can paint the table background + // in its own display item, so do that to take advantage of + // opacity and visibility optimizations + if (deflate == nsMargin(0, 0, 0, 0)) { + DisplayBackgroundUnconditional(aBuilder, aLists, false); + } + } + + // This background is created if any of the table parts are visible, + // or if we're doing event handling (since DisplayGenericTablePart + // needs the item for the |sortEventBackgrounds|-dependent code). + // Specific visibility decisions are delegated to the table background + // painter, which handles borders and backgrounds for the table. + if (aBuilder->IsForEventDelivery() || + AnyTablePartHasBorderOrBackground(this, this, GetNextSibling()) || + AnyTablePartHasBorderOrBackground(this, mColGroups.FirstChild(), nullptr)) { + item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this, + deflate != nsMargin(0, 0, 0, 0)); + aLists.BorderBackground()->AppendNewToTop(item); + } + } + DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); + if (item) { + UpdateItemForColGroupBackgrounds(item, mColGroups); + } } nsMargin diff --git a/layout/tables/nsTableFrame.h b/layout/tables/nsTableFrame.h index a15015431c766..48bc868ca3aaa 100644 --- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -245,6 +245,7 @@ class nsTableFrame : public nsContainerFrame nsFrame* aFrame, const nsRect& aDirtyRect, const nsDisplayListSet& aLists, + nsDisplayTableItem* aDisplayItem, DisplayGenericTablePartTraversal aTraversal = GenericTraversal); // Return the closest sibling of aPriorChildFrame (including aPriroChildFrame) diff --git a/layout/tables/nsTableRowFrame.cpp b/layout/tables/nsTableRowFrame.cpp index 509c572e23ee8..6ce8ed5791ea4 100644 --- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -621,7 +621,21 @@ nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { - nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists); + nsDisplayTableItem* item = nullptr; + if (IsVisibleInSelection(aBuilder)) { + bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); + if (isRoot) { + // This background is created regardless of whether this frame is + // visible or not. Visibility decisions are delegated to the + // table background painter. + // We would use nsDisplayGeneric for this rare case except that we + // need the background to be larger than the row frame in some + // cases. + item = new (aBuilder) nsDisplayTableRowBackground(aBuilder, this); + aLists.BorderBackground()->AppendNewToTop(item); + } + } + nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); } nsIFrame::LogicalSides diff --git a/layout/tables/nsTableRowGroupFrame.cpp b/layout/tables/nsTableRowGroupFrame.cpp index 9d0b575c59db3..61d05d3066350 100644 --- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -293,8 +293,19 @@ nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsRect& aDirtyRect, const nsDisplayListSet& aLists) { + nsDisplayTableItem* item = nullptr; + if (IsVisibleInSelection(aBuilder)) { + bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext(); + if (isRoot) { + // This background is created regardless of whether this frame is + // visible or not. Visibility decisions are delegated to the + // table background painter. + item = new (aBuilder) nsDisplayTableRowGroupBackground(aBuilder, this); + aLists.BorderBackground()->AppendNewToTop(item); + } + } nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, - aLists, DisplayRows); + aLists, item, DisplayRows); } nsIFrame::LogicalSides diff --git a/layout/tables/nsTableWrapperFrame.cpp b/layout/tables/nsTableWrapperFrame.cpp index 0cdfcde1ec92d..769ca596b3b8b 100644 --- a/layout/tables/nsTableWrapperFrame.cpp +++ b/layout/tables/nsTableWrapperFrame.cpp @@ -190,11 +190,7 @@ nsTableWrapperFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, // Now we have to sort everything by content order, since the caption // may be somewhere inside the table - set.BlockBorderBackgrounds()->SortByContentOrder(GetContent()); - set.Floats()->SortByContentOrder(GetContent()); - set.Content()->SortByContentOrder(GetContent()); - set.PositionedDescendants()->SortByContentOrder(GetContent()); - set.Outlines()->SortByContentOrder(GetContent()); + set.SortAllByContentOrder(GetContent()); set.MoveTo(aLists); } From d92929200cfafc2a0c4d8daaf1135333ec53dd03 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:44:35 +0200 Subject: [PATCH 063/131] Bug 1361443 - nsMultiplexInputStream should implement nsIAsyncInputStream, r=smaug --- xpcom/io/nsMultiplexInputStream.cpp | 216 +++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 3 deletions(-) diff --git a/xpcom/io/nsMultiplexInputStream.cpp b/xpcom/io/nsMultiplexInputStream.cpp index de0c10df6cb64..24b6805f51352 100644 --- a/xpcom/io/nsMultiplexInputStream.cpp +++ b/xpcom/io/nsMultiplexInputStream.cpp @@ -12,6 +12,7 @@ #include "mozilla/Attributes.h" #include "mozilla/MathAlgorithms.h" #include "mozilla/Mutex.h" +#include "mozilla/SystemGroup.h" #include "base/basictypes.h" @@ -24,6 +25,7 @@ #include "nsIClassInfoImpl.h" #include "nsIIPCSerializableInputStream.h" #include "mozilla/ipc/InputStreamUtils.h" +#include "nsIAsyncInputStream.h" using namespace mozilla; using namespace mozilla::ipc; @@ -38,6 +40,7 @@ class nsMultiplexInputStream final , public nsISeekableStream , public nsIIPCSerializableInputStream , public nsICloneableInputStream + , public nsIAsyncInputStream { public: nsMultiplexInputStream(); @@ -48,6 +51,9 @@ class nsMultiplexInputStream final NS_DECL_NSISEEKABLESTREAM NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM NS_DECL_NSICLONEABLEINPUTSTREAM + NS_DECL_NSIASYNCINPUTSTREAM + + void AsyncWaitCompleted(); private: ~nsMultiplexInputStream() @@ -70,12 +76,14 @@ class nsMultiplexInputStream final bool IsSeekable() const; bool IsIPCSerializable() const; bool IsCloneable() const; + bool IsAsyncInputStream() const; Mutex mLock; // Protects access to all data members. nsTArray> mStreams; uint32_t mCurrentStream; bool mStartedReadingCurrent; nsresult mStatus; + nsCOMPtr mAsyncWaitCallback; }; NS_IMPL_ADDREF(nsMultiplexInputStream) @@ -86,12 +94,14 @@ NS_IMPL_CLASSINFO(nsMultiplexInputStream, nullptr, nsIClassInfo::THREADSAFE, NS_INTERFACE_MAP_BEGIN(nsMultiplexInputStream) NS_INTERFACE_MAP_ENTRY(nsIMultiplexInputStream) - NS_INTERFACE_MAP_ENTRY(nsIInputStream) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIInputStream, nsIMultiplexInputStream) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream, IsIPCSerializable()) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream, IsCloneable()) + NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAsyncInputStream, + IsAsyncInputStream()) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMultiplexInputStream) NS_IMPL_QUERY_CLASSINFO(nsMultiplexInputStream) NS_INTERFACE_MAP_END @@ -216,6 +226,9 @@ nsMultiplexInputStream::Close() rv = rv2; } } + + mAsyncWaitCallback = nullptr; + return rv; } @@ -308,7 +321,7 @@ nsMultiplexInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, nsresult rv = NS_OK; ReadSegmentsState state; - state.mThisStream = this; + state.mThisStream = static_cast(this); state.mOffset = 0; state.mWriter = aWriter; state.mClosure = aClosure; @@ -677,6 +690,189 @@ nsMultiplexInputStream::SetEOF() return NS_ERROR_NOT_IMPLEMENTED; } +NS_IMETHODIMP +nsMultiplexInputStream::CloseWithStatus(nsresult aStatus) +{ + return Close(); +} + +// This class is used to inform nsMultiplexInputStream that it's time to execute +// the asyncWait callback. +class AsyncWaitRunnable final : public Runnable +{ + RefPtr mStream; + +public: + explicit AsyncWaitRunnable(nsMultiplexInputStream* aStream) + : Runnable("AsyncWaitRunnable") + , mStream(aStream) + { + MOZ_ASSERT(aStream); + } + + NS_IMETHOD + Run() override + { + mStream->AsyncWaitCompleted(); + return NS_OK; + } +}; + +// This helper class processes an array of nsIAsyncInputStreams, calling +// AsyncWait() for each one of them. When all of them have answered, this helper +// dispatches a AsyncWaitRunnable object. +class AsyncStreamHelper final : public nsIInputStreamCallback +{ +public: + NS_DECL_THREADSAFE_ISUPPORTS + + static nsresult + Process(nsMultiplexInputStream* aStream, + nsTArray>& aAsyncStreams, + uint32_t aFlags, uint32_t aRequestedCount, + nsIEventTarget* aEventTarget) + { + MOZ_ASSERT(aStream); + MOZ_ASSERT(!aAsyncStreams.IsEmpty()); + MOZ_ASSERT(aEventTarget); + + RefPtr helper = + new AsyncStreamHelper(aStream, aAsyncStreams, aEventTarget); + return helper->Run(aFlags, aRequestedCount); + } + +private: + AsyncStreamHelper(nsMultiplexInputStream* aStream, + nsTArray>& aAsyncStreams, + nsIEventTarget* aEventTarget) + : mMutex("AsyncStreamHelper::mMutex") + , mStream(aStream) + , mEventTarget(aEventTarget) + , mValid(true) + { + mPendingStreams.SwapElements(aAsyncStreams); + } + + ~AsyncStreamHelper() = default; + + nsresult + Run(uint32_t aFlags, uint32_t aRequestedCount) + { + MutexAutoLock lock(mMutex); + + for (uint32_t i = 0; i < mPendingStreams.Length(); ++i) { + nsresult rv = + mPendingStreams[i]->AsyncWait(this, aFlags, aRequestedCount, + mEventTarget); + if (NS_WARN_IF(NS_FAILED(rv))) { + mValid = true; + return rv; + } + } + + return NS_OK; + } + + NS_IMETHOD + OnInputStreamReady(nsIAsyncInputStream* aStream) override + { + MOZ_ASSERT(aStream, "This cannot be one of ours."); + + MutexAutoLock lock(mMutex); + + // We failed during the Run(). + if (!mValid) { + return NS_OK; + } + + MOZ_ASSERT(mPendingStreams.Contains(aStream)); + mPendingStreams.RemoveElement(aStream); + + // The last asyncStream answered. We can inform nsMultiplexInputStream. + if (mPendingStreams.IsEmpty()) { + RefPtr runnable = new AsyncWaitRunnable(mStream); + return mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); + } + + return NS_OK; + } + + Mutex mMutex; + RefPtr mStream; + nsTArray> mPendingStreams; + nsCOMPtr mEventTarget; + bool mValid; +}; + +NS_IMPL_ISUPPORTS(AsyncStreamHelper, nsIInputStreamCallback) + +NS_IMETHODIMP +nsMultiplexInputStream::AsyncWait(nsIInputStreamCallback* aCallback, + uint32_t aFlags, + uint32_t aRequestedCount, + nsIEventTarget* aEventTarget) +{ + // When AsyncWait() is called, it's better to call AsyncWait() to any sub + // stream if they are valid nsIAsyncInputStream instances. In this way, when + // they all call OnInputStreamReady(), we can proceed with the Read(). + + MutexAutoLock lock(mLock); + + if (NS_FAILED(mStatus)) { + return mStatus; + } + + if (mAsyncWaitCallback && aCallback) { + return NS_ERROR_FAILURE; + } + + mAsyncWaitCallback = aCallback; + + if (!mAsyncWaitCallback) { + return NS_OK; + } + + nsTArray> asyncStreams; + for (uint32_t i = mCurrentStream; i < mStreams.Length(); ++i) { + nsCOMPtr asyncStream = + do_QueryInterface(mStreams.SafeElementAt(i, nullptr)); + if (asyncStream) { + asyncStreams.AppendElement(asyncStream); + } + } + + if (!aEventTarget) { + aEventTarget = SystemGroup::EventTargetFor(TaskCategory::Other); + } + + if (asyncStreams.IsEmpty()) { + RefPtr runnable = new AsyncWaitRunnable(this); + return aEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); + } + + return AsyncStreamHelper::Process(this, asyncStreams, aFlags, aRequestedCount, + aEventTarget); +} + +void +nsMultiplexInputStream::AsyncWaitCompleted() +{ + nsCOMPtr callback; + + { + MutexAutoLock lock(mLock); + + // The callback has been nullified in the meantime. + if (!mAsyncWaitCallback) { + return; + } + + mAsyncWaitCallback.swap(callback); + } + + callback->OnInputStreamReady(this); +} + nsresult nsMultiplexInputStreamConstructor(nsISupports* aOuter, REFNSIID aIID, @@ -820,7 +1016,7 @@ nsMultiplexInputStream::Clone(nsIInputStream** aClone) return NS_ERROR_FAILURE; } - RefPtr clone = new nsMultiplexInputStream(); + nsCOMPtr clone = new nsMultiplexInputStream(); nsresult rv; uint32_t len = mStreams.Length(); @@ -881,3 +1077,17 @@ nsMultiplexInputStream::IsCloneable() const } return true; } + +bool +nsMultiplexInputStream::IsAsyncInputStream() const +{ + // nsMultiplexInputStream is nsIAsyncInputStream if at least 1 of the + // substream implements that interface. + for (uint32_t i = 0, len = mStreams.Length(); i < len; ++i) { + nsCOMPtr substream = do_QueryInterface(mStreams[i]); + if (substream) { + return true; + } + } + return false; +} From 763ecb9f2907760b870d8d9ef912f7347185eb31 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:44:53 +0200 Subject: [PATCH 064/131] Bug 1361443 - Tests for remote blobs and multipart inputStreams, r=smaug --- dom/file/ipc/tests/browser_ipcBlob.js | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/dom/file/ipc/tests/browser_ipcBlob.js b/dom/file/ipc/tests/browser_ipcBlob.js index 5ca04b1dbe459..113509a60599a 100644 --- a/dom/file/ipc/tests/browser_ipcBlob.js +++ b/dom/file/ipc/tests/browser_ipcBlob.js @@ -171,3 +171,36 @@ add_task(function* test_CtoPtoC_bc_small() { yield BrowserTestUtils.removeTab(tab1); yield BrowserTestUtils.removeTab(tab2); }); + +// Multipart Blob childA-parent-childB. +add_task(function* test_CtoPtoC_multipart() { + let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); + let browser1 = gBrowser.getBrowserForTab(tab1); + + let blob = yield ContentTask.spawn(browser1, null, function() { + return new Blob(["!"]); + }); + + ok(blob, "CtoPtoC-,ultipart: We have a blob!"); + is(blob.size, "!".length, "CtoPtoC-multipart: The size matches"); + + let newBlob = new Blob(["world", blob]); + + let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI); + let browser2 = gBrowser.getBrowserForTab(tab2); + + let status = yield ContentTask.spawn(browser2, newBlob, function(blob) { + return new Promise(resolve => { + let fr = new content.FileReader(); + fr.readAsText(new Blob(["hello ", blob])); + fr.onloadend = function() { + resolve(fr.result == "hello world!"); + } + }); + }); + + ok(status, "CtoPtoC-multipart: Data match!"); + + yield BrowserTestUtils.removeTab(tab1); + yield BrowserTestUtils.removeTab(tab2); +}); From 18d973995ce70784615679483ab46ce19288bb9a Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:45:34 +0200 Subject: [PATCH 065/131] Bug 1361443 - FileReader should support ReadSegments() not returning the whole size, r=smaug --- dom/file/FileReader.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/dom/file/FileReader.cpp b/dom/file/FileReader.cpp index 37bdbe7fbc5f5..e8e3fa4f66e4d 100644 --- a/dom/file/FileReader.cpp +++ b/dom/file/FileReader.cpp @@ -280,6 +280,8 @@ FileReader::DoReadData(uint64_t aCount) { MOZ_ASSERT(mAsyncStream); + uint32_t bytesRead = 0; + if (mDataFormat == FILE_AS_BINARY) { //Continuously update our binary string as data comes in uint32_t oldLen = mResult.Length(); @@ -301,14 +303,13 @@ FileReader::DoReadData(uint64_t aCount) NS_ENSURE_SUCCESS(rv, rv); } - uint32_t bytesRead = 0; rv = mBufferedStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - MOZ_ASSERT(bytesRead == aCount, "failed to read data"); + mResult.Truncate(oldLen + bytesRead); } else { CheckedInt size = mDataLen; @@ -322,22 +323,16 @@ FileReader::DoReadData(uint64_t aCount) return NS_ERROR_OUT_OF_MEMORY; } - if (mDataFormat != FILE_AS_ARRAYBUFFER) { - mFileData = (char *) realloc(mFileData, mDataLen + aCount); - NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY); - } - - uint32_t bytesRead = 0; MOZ_DIAGNOSTIC_ASSERT(mFileData); + MOZ_RELEASE_ASSERT((mDataLen + aCount) <= mTotal); + nsresult rv = mAsyncStream->Read(mFileData + mDataLen, aCount, &bytesRead); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } - - MOZ_ASSERT(bytesRead == aCount, "failed to read data"); } - mDataLen += aCount; + mDataLen += bytesRead; return NS_OK; } @@ -416,8 +411,15 @@ FileReader::ReadFileContent(Blob& aBlob, return; } - if (mDataFormat == FILE_AS_ARRAYBUFFER) { - mFileData = js_pod_malloc(mTotal); + // Binary Format doesn't need a post-processing of the data. Everything is + // written directly into mResult. + if (mDataFormat != FILE_AS_BINARY) { + if (mDataFormat == FILE_AS_ARRAYBUFFER) { + mFileData = js_pod_malloc(mTotal); + } else { + mFileData = (char *) malloc(mTotal); + } + if (!mFileData) { NS_WARNING("Preallocation failed for ReadFileData"); aRv.Throw(NS_ERROR_OUT_OF_MEMORY); From ae860ef560020c2bd72d3dc433fe5c10045d502a Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:49:56 +0200 Subject: [PATCH 066/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 1 - SyncRead should read as much as required, r=smaug --- dom/workers/FileReaderSync.cpp | 38 +++++++++++++++++++++++++++------- dom/workers/FileReaderSync.h | 4 ++-- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 4bf8e5fd6d065..8214f81356a43 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -78,7 +78,7 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, } uint32_t numRead; - aRv = Read(stream, bufferData.get(), blobSize, &numRead); + aRv = SyncRead(stream, bufferData.get(), blobSize, &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -110,7 +110,7 @@ FileReaderSync::ReadAsBinaryString(Blob& aBlob, uint32_t numRead; do { char readBuf[4096]; - aRv = Read(stream, readBuf, sizeof(readBuf), &numRead); + aRv = SyncRead(stream, readBuf, sizeof(readBuf), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -145,7 +145,7 @@ FileReaderSync::ReadAsText(Blob& aBlob, } uint32_t numRead = 0; - aRv = Read(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); + aRv = SyncRead(stream, sniffBuf.BeginWriting(), sniffBuf.Length(), &numRead); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -361,8 +361,8 @@ NS_INTERFACE_MAP_END } // anonymous nsresult -FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, - uint32_t* aRead) +FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, + uint32_t aBufferSize, uint32_t* aRead) { MOZ_ASSERT(aStream); MOZ_ASSERT(aBuffer); @@ -370,10 +370,34 @@ FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSiz // Let's try to read, directly. nsresult rv = aStream->Read(aBuffer, aBufferSize, aRead); - if (NS_SUCCEEDED(rv) || rv != NS_BASE_STREAM_WOULD_BLOCK) { + + // Nothing else to read. + if (rv == NS_BASE_STREAM_CLOSED || + (NS_SUCCEEDED(rv) && *aRead == 0)) { + return NS_OK; + } + + // An error. + if (NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) { return rv; } + // All good. + if (NS_SUCCEEDED(rv)) { + // Not enough data, let's read recursively. + if (*aRead != aBufferSize) { + uint32_t byteRead = 0; + rv = SyncRead(aStream, aBuffer + *aRead, aBufferSize - *aRead, &byteRead); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + *aRead += byteRead; + } + + return NS_OK; + } + // We need to proceed async. nsCOMPtr asyncStream = do_QueryInterface(aStream); if (!asyncStream) { @@ -408,5 +432,5 @@ FileReaderSync::Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSiz } // Now, we can try to read again. - return Read(aStream, aBuffer, aBufferSize, aRead); + return SyncRead(aStream, aBuffer, aBufferSize, aRead); } diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index c23803161b925..a23877a60abc2 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,8 +32,8 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); - nsresult Read(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, - uint32_t* aRead); + nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, + uint32_t aBufferSize, uint32_t* aRead); public: static already_AddRefed From b84f638069c25d1eee4e2f0bfd93878c58eea831 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:50:25 +0200 Subject: [PATCH 067/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 2 - nsIConverterInputStream needs a syncInputStream, r=smaug --- dom/workers/FileReaderSync.cpp | 65 +++++++++++++++++++++++++++++----- dom/workers/FileReaderSync.h | 3 ++ 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 8214f81356a43..9f9a008666d0b 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -182,16 +182,10 @@ FileReaderSync::ReadAsText(Blob& aBlob, } // Let's recreate the full stream using a: - // multiplexStream(stringStream + original stream) + // multiplexStream(syncStream + original stream) // In theory, we could try to see if the inputStream is a nsISeekableStream, // but this doesn't work correctly for nsPipe3 - See bug 1349570. - nsCOMPtr stringStream; - aRv = NS_NewCStringInputStream(getter_AddRefs(stringStream), sniffBuf); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - nsCOMPtr multiplexStream = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); if (NS_WARN_IF(!multiplexStream)) { @@ -199,12 +193,24 @@ FileReaderSync::ReadAsText(Blob& aBlob, return; } - aRv = multiplexStream->AppendStream(stringStream); + nsCOMPtr sniffStringStream; + aRv = NS_NewCStringInputStream(getter_AddRefs(sniffStringStream), sniffBuf); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + aRv = multiplexStream->AppendStream(sniffStringStream); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsCOMPtr syncStream; + aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); if (NS_WARN_IF(aRv.Failed())) { return; } - aRv = multiplexStream->AppendStream(stream); + aRv = multiplexStream->AppendStream(syncStream); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -434,3 +440,44 @@ FileReaderSync::SyncRead(nsIInputStream* aStream, char* aBuffer, // Now, we can try to read again. return SyncRead(aStream, aBuffer, aBufferSize, aRead); } + +nsresult +FileReaderSync::ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, + nsIInputStream** aSyncStream) +{ + // If the stream is not async, we have nothing to do here. + nsCOMPtr asyncStream = do_QueryInterface(aAsyncStream); + if (!asyncStream) { + nsCOMPtr stream = aAsyncStream; + stream.forget(aSyncStream); + return NS_OK; + } + + uint64_t length; + nsresult rv = aAsyncStream->Available(&length); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsAutoCString buffer; + if (!buffer.SetLength(length, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t read; + rv = SyncRead(aAsyncStream, buffer.BeginWriting(), length, &read); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + if (read != length) { + return NS_ERROR_FAILURE; + } + + rv = NS_NewCStringInputStream(aSyncStream, buffer); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + return NS_OK; +} diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index a23877a60abc2..6b61f039ee8ab 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -32,6 +32,9 @@ class FileReaderSync final nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, nsAString &aResult); + nsresult ConvertAsyncToSyncStream(nsIInputStream* aAsyncStream, + nsIInputStream** aSyncStream); + nsresult SyncRead(nsIInputStream* aStream, char* aBuffer, uint32_t aBufferSize, uint32_t* aRead); From 78274873fe60deda335f01f39cb283bd20564304 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:50:46 +0200 Subject: [PATCH 068/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 2 - tests, r=smaug --- .../tests/test_ipcBlob_fileReaderSync.html | 69 ++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html index 814e49c71b41b..2a628987d6aaf 100644 --- a/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html +++ b/dom/file/ipc/tests/test_ipcBlob_fileReaderSync.html @@ -13,21 +13,74 @@ onmessage = function(event) { let readerMemoryBlob = new FileReaderSync(); let status = readerMemoryBlob.readAsText(new Blob(['hello world'])) == 'hello world'; + postMessage({ status, message: "FileReaderSync with memory blob still works" }); - let readerIPCBlob = new FileReaderSync(); - postMessage({ blob: event.data, data: readerIPCBlob.readAsText(event.data), status }); + let readerIPCBlob1 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsText', + data: readerIPCBlob1.readAsText(event.data)}); + + let readerIPCBlob2 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsArrayBuffer', + data: readerIPCBlob2.readAsArrayBuffer(event.data)}); + + let readerIPCBlob3 = new FileReaderSync(); + postMessage({ blob: event.data, method: 'readAsDataURL', + data: readerIPCBlob3.readAsDataURL(event.data)}); + + let multipartBlob = new Blob(['wow', event.data]); + + let readerIPCBlobMultipart1 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsText', + data: readerIPCBlobMultipart1.readAsText(multipartBlob)}); + + let readerIPCBlobMultipart2 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsArrayBuffer', + data: readerIPCBlobMultipart2.readAsArrayBuffer(multipartBlob)}); + + let readerIPCBlobMultipart3 = new FileReaderSync(); + postMessage({ blob: multipartBlob, method: 'readAsDataURL', + data: readerIPCBlobMultipart3.readAsDataURL(multipartBlob)}); + + postMessage({ finish: true }); + } +} + +let completed = false; +let pendingTasks = 0; +function maybeFinish() { + if (completed && !pendingTasks) { + SimpleTest.finish(); } } let workerUrl = URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"])); let worker = new Worker(workerUrl); worker.onmessage = event => { - let fr = new FileReader(); - fr.readAsText(event.data.blob); - fr.onload = () => { - is(event.data.data, fr.result, "The file has been read"); - ok(event.data.status, "FileReaderSync with memory blob still works"); - SimpleTest.finish(); + if ("status" in event.data) { + ok(event.data.status, event.data.message); + return; + } + + if ("blob" in event.data) { + let fr = new FileReader(); + fr[event.data.method](event.data.blob); + ++pendingTasks; + fr.onload = () => { + if (event.data.method != 'readAsArrayBuffer') { + is(event.data.data, fr.result, "The file has been read"); + } else { + is(event.data.data.byteLength, fr.result.byteLength, "The file has been read"); + } + --pendingTasks; + maybeFinish(); + } + + return; + } + + if ("finish" in event.data) { + completed = true; + maybeFinish(); } }; From 3d4a14ab5243f1c6c2675b8421d5b6a8b6298a3d Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 14:51:11 +0200 Subject: [PATCH 069/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 3 - Base64EncodeInputStream needs a sync inputStream, r=smaug --- dom/workers/FileReaderSync.cpp | 47 +++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 9f9a008666d0b..c711232cf6d36 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -21,6 +21,7 @@ #include "nsIConverterInputStream.h" #include "nsIInputStream.h" #include "nsIMultiplexInputStream.h" +#include "nsStreamUtils.h" #include "nsStringStream.h" #include "nsISupportsImpl.h" #include "nsNetUtil.h" @@ -82,7 +83,12 @@ FileReaderSync::ReadAsArrayBuffer(JSContext* aCx, if (NS_WARN_IF(aRv.Failed())) { return; } - NS_ASSERTION(numRead == blobSize, "failed to read data"); + + // The file is changed in the meantime? + if (numRead != blobSize) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get()); if (!arrayBuffer) { @@ -150,6 +156,12 @@ FileReaderSync::ReadAsText(Blob& aBlob, return; } + // No data, we don't need to continue. + if (numRead == 0) { + aResult.Truncate(); + return; + } + // The BOM sniffing is baked into the "decode" part of the Encoding // Standard, which the File API references. if (!nsContentUtils::CheckForBOM((const unsigned char*)sniffBuf.BeginReading(), @@ -244,19 +256,42 @@ FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, return; } - uint64_t size = aBlob.GetSize(aRv); - if (NS_WARN_IF(aRv.Failed())){ + nsCOMPtr syncStream; + aRv = ConvertAsyncToSyncStream(stream, getter_AddRefs(syncStream)); + if (NS_WARN_IF(aRv.Failed())) { return; } - nsCOMPtr bufferedStream; - aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size); + // We need a buffered stream. + if (!NS_InputStreamIsBuffered(syncStream)) { + nsCOMPtr bufferedStream; + aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), + syncStream, 4096); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + syncStream = bufferedStream; + } + + uint64_t size; + aRv = syncStream->Available(&size); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + uint64_t blobSize = aBlob.GetSize(aRv); if (NS_WARN_IF(aRv.Failed())){ return; } + // The file is changed in the meantime? + if (blobSize != size) { + return; + } + nsAutoString encodedData; - aRv = Base64EncodeInputStream(bufferedStream, encodedData, size); + aRv = Base64EncodeInputStream(syncStream, encodedData, size); if (NS_WARN_IF(aRv.Failed())){ return; } From 82e62419c3cb8d2a215e6cd082a003d578eb429f Mon Sep 17 00:00:00 2001 From: Andreas Farre Date: Tue, 2 May 2017 08:10:00 -0400 Subject: [PATCH 070/131] Bug 1322184 - Measure time spent in content JS that causes delay in paint. r=billm, data-r=bsmedberg MozReview-Commit-ID: Iz31CKSnDdc --- dom/base/TabGroup.cpp | 14 ++- dom/base/TabGroup.h | 6 +- ipc/glue/BackgroundChildImpl.cpp | 13 +++ ipc/glue/BackgroundChildImpl.h | 5 + ipc/glue/MessageChannel.cpp | 2 + ipc/glue/ProtocolUtils.h | 1 + layout/ipc/VsyncChild.cpp | 3 + toolkit/components/telemetry/Histograms.json | 47 ++++++++ xpcom/threads/SchedulerGroup.cpp | 116 ++++++++++++++++++- xpcom/threads/SchedulerGroup.h | 9 ++ 10 files changed, 212 insertions(+), 4 deletions(-) diff --git a/dom/base/TabGroup.cpp b/dom/base/TabGroup.cpp index 8847d3b65c717..d1a5ec5f3d3ba 100644 --- a/dom/base/TabGroup.cpp +++ b/dom/base/TabGroup.cpp @@ -221,7 +221,7 @@ TabGroup::FindItemWithName(const nsAString& aName, } nsTArray -TabGroup::GetTopLevelWindows() +TabGroup::GetTopLevelWindows() const { MOZ_ASSERT(NS_IsMainThread()); nsTArray array; @@ -263,5 +263,17 @@ TabGroup::AbstractMainThreadForImpl(TaskCategory aCategory) return SchedulerGroup::AbstractMainThreadForImpl(aCategory); } +bool +TabGroup::IsBackground() const +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + for (nsPIDOMWindowOuter* outerWindow : GetTopLevelWindows()) { + if (!outerWindow->IsBackground()) { + return false; + } + } + return true; +} } // namespace dom } // namespace mozilla diff --git a/dom/base/TabGroup.h b/dom/base/TabGroup.h index 08dd0051dd5a1..75ae77c1f6d73 100644 --- a/dom/base/TabGroup.h +++ b/dom/base/TabGroup.h @@ -114,13 +114,17 @@ class TabGroup final : public SchedulerGroup nsIDocShellTreeItem* aOriginalRequestor, nsIDocShellTreeItem** aFoundItem); - nsTArray GetTopLevelWindows(); + nsTArray GetTopLevelWindows() const; const nsTArray& GetWindows() { return mWindows; } // This method is always safe to call off the main thread. The nsIEventTarget // can always be used off the main thread. nsIEventTarget* EventTargetFor(TaskCategory aCategory) const override; + // Returns true if all of the TabGroup's top-level windows are in + // the background. + bool IsBackground() const override; + private: virtual AbstractThread* AbstractMainThreadForImpl(TaskCategory aCategory) override; diff --git a/ipc/glue/BackgroundChildImpl.cpp b/ipc/glue/BackgroundChildImpl.cpp index 6904e91b3de6f..d282eadba6468 100644 --- a/ipc/glue/BackgroundChildImpl.cpp +++ b/ipc/glue/BackgroundChildImpl.cpp @@ -15,6 +15,7 @@ #endif #include "mozilla/media/MediaChild.h" #include "mozilla/Assertions.h" +#include "mozilla/SchedulerGroup.h" #include "mozilla/dom/PBlobChild.h" #include "mozilla/dom/PFileSystemRequestChild.h" #include "mozilla/dom/FileSystemTaskBase.h" @@ -547,6 +548,18 @@ BackgroundChildImpl::DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* a return true; } +#ifdef EARLY_BETA_OR_EARLIER +void +BackgroundChildImpl::OnChannelReceivedMessage(const Message& aMsg) +{ + if (aMsg.type() == layout::PVsync::MessageType::Msg_Notify__ID) { + // Not really necessary to look at the message payload, it will be + // <0.5ms away from TimeStamp::Now() + SchedulerGroup::MarkVsyncReceived(); + } +} +#endif + } // namespace ipc } // namespace mozilla diff --git a/ipc/glue/BackgroundChildImpl.h b/ipc/glue/BackgroundChildImpl.h index 20ca291274e6e..3c0e1162755b0 100644 --- a/ipc/glue/BackgroundChildImpl.h +++ b/ipc/glue/BackgroundChildImpl.h @@ -197,6 +197,11 @@ class BackgroundChildImpl : public PBackgroundChild virtual bool DeallocPGamepadTestChannelChild(PGamepadTestChannelChild* aActor) override; + +#ifdef EARLY_BETA_OR_EARLIER + virtual void + OnChannelReceivedMessage(const Message& aMsg) override; +#endif }; class BackgroundChildImpl::ThreadLocal final diff --git a/ipc/glue/MessageChannel.cpp b/ipc/glue/MessageChannel.cpp index 39c30d88194a4..14f8785cb9a53 100644 --- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -1088,6 +1088,8 @@ MessageChannel::OnMessageReceivedFromLink(Message&& aMsg) if (MaybeInterceptSpecialIOMessage(aMsg)) return; + mListener->OnChannelReceivedMessage(aMsg); + // Regardless of the Interrupt stack, if we're awaiting a sync reply, // we know that it needs to be immediately handled to unblock us. if (aMsg.is_sync() && aMsg.is_reply()) { diff --git a/ipc/glue/ProtocolUtils.h b/ipc/glue/ProtocolUtils.h index 0e9b72aff32b9..81185748fad32 100644 --- a/ipc/glue/ProtocolUtils.h +++ b/ipc/glue/ProtocolUtils.h @@ -376,6 +376,7 @@ class IToplevelProtocol : public IProtocol virtual nsIEventTarget* GetActorEventTarget(); + virtual void OnChannelReceivedMessage(const Message& aMsg) {} protected: // Override this method in top-level protocols to change the event target // for a new actor (and its sub-actors). diff --git a/layout/ipc/VsyncChild.cpp b/layout/ipc/VsyncChild.cpp index 84641e17a609f..9bb3d85eee248 100644 --- a/layout/ipc/VsyncChild.cpp +++ b/layout/ipc/VsyncChild.cpp @@ -5,6 +5,7 @@ #include "VsyncChild.h" +#include "mozilla/SchedulerGroup.h" #include "mozilla/VsyncDispatcher.h" #include "nsThreadUtils.h" @@ -60,6 +61,8 @@ VsyncChild::RecvNotify(const TimeStamp& aVsyncTimestamp) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(!mIsShutdown); + + SchedulerGroup::MarkVsyncRan(); if (mObservingVsync && mObserver) { mObserver->NotifyVsync(aVsyncTimestamp); } diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 3b3403712d345..4c5083074c1db 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -283,6 +283,53 @@ "high": 1000, "n_buckets": 50 }, + "CONTENT_JS_FOREGROUND_TICK_DELAY_TOTAL_MS": { + "alert_emails": ["farre@mozilla.com"], + "expires_in_version": "61", + "kind": "exponential", + "high": 100, + "n_buckets": 10, + "bug_numbers": [1305153, 1296486], + "description": "Time (in ms) that a pending vsync gets delayed by a runnable associated with a TabGroup that is (partially) in the foreground. The time is measured from the vsync event's time stamp to when the runnable finishes execution." + }, + "CONTENT_JS_FOREGROUND_TICK_DELAY_EVENTS_MS": { + "alert_emails": ["farre@mozilla.com"], + "expires_in_version": "61", + "keyed": true, + "kind": "exponential", + "high": 100, + "n_buckets": 10, + "bug_numbers": [1305153, 1296486], + "description": "Time (in ms) that a pending vsync gets delayed by a runnable associated with a TabGroup that is (partially) in the foreground. The time is measured from the vsync event's time stamp to when the runnable finishes execution. The histogram is keyed by the label of the runnable, indicating which type of task the runnable is performing." + }, + "CONTENT_JS_BACKGROUND_TICK_DELAY_TOTAL_MS": { + "alert_emails": ["farre@mozilla.com"], + "expires_in_version": "61", + "kind": "exponential", + "high": 100, + "n_buckets": 10, + "bug_numbers": [1305153, 1296486], + "description": "Time (in ms) that a pending vsync gets delayed by a runnable associated with a TabGroup that is completely in the background. The time is measured from the vsync event's time stamp to when the runnable finishes execution." + }, + "CONTENT_JS_BACKGROUND_TICK_DELAY_EVENTS_MS": { + "alert_emails": ["farre@mozilla.com"], + "expires_in_version": "61", + "keyed": true, + "kind": "exponential", + "high": 100, + "n_buckets": 10, + "bug_numbers": [1305153, 1296486], + "description" : "Time (in ms) that a pending vsync gets delayed by a runnable associated with a TabGroup that is completely in the background. The time is measured from the vsync event's time stamp to when the runnable finishes execution. The histogram is keyed by the label of the runnable, indicating which type of task the runnable is performing." + }, + "CONTENT_JS_KNOWN_TICK_DELAY_MS": { + "alert_emails": ["farre@mozilla.com"], + "expires_in_version": "65", + "kind": "exponential", + "high": 100, + "n_buckets": 10, + "bug_numbers": [1305153, 1296486], + "description": "Time (in ms) that a pending vsync gets delayed by a runnable associated with a TabGroup when the vsync event's timestamp is before the starting time of the runnable. The time is measured from the vsync event's time stamp to when the runnable finishes execution." + }, "COMPOSITOR_ANIMATION_DURATION" : { "expires_in_version": "58", "description": "Duration of animations running on the compositor in milliseconds", diff --git a/xpcom/threads/SchedulerGroup.cpp b/xpcom/threads/SchedulerGroup.cpp index bdbb1b39dc5ed..856fa8f9ceb64 100644 --- a/xpcom/threads/SchedulerGroup.cpp +++ b/xpcom/threads/SchedulerGroup.cpp @@ -8,12 +8,15 @@ #include "jsfriendapi.h" #include "mozilla/AbstractThread.h" +#include "mozilla/Atomics.h" #include "mozilla/Move.h" #include "nsINamed.h" #include "nsQueryObject.h" #include "mozilla/dom/ScriptSettings.h" #include "nsThreadUtils.h" +#include "mozilla/Telemetry.h" + using namespace mozilla; class SchedulerGroup::Runnable final : public mozilla::Runnable @@ -37,8 +40,9 @@ class SchedulerGroup::Runnable final : public mozilla::Runnable return NS_OK; } - NS_DECL_NSIRUNNABLE + bool IsBackground() const { return mDispatcher->IsBackground(); } + NS_DECL_NSIRUNNABLE private: nsCOMPtr mRunnable; RefPtr mDispatcher; @@ -76,6 +80,84 @@ class SchedulerEventTarget final : public nsIEventTarget NS_DEFINE_STATIC_IID_ACCESSOR(SchedulerEventTarget, NS_DISPATCHEREVENTTARGET_IID) +static Atomic gEarliestUnprocessedVsync(0); + +class MOZ_RAII AutoCollectVsyncTelemetry final +{ +public: + explicit AutoCollectVsyncTelemetry(SchedulerGroup::Runnable* aRunnable + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mIsBackground(aRunnable->IsBackground()) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; +#ifdef EARLY_BETA_OR_EARLIER + aRunnable->GetName(mKey); + mStart = TimeStamp::Now(); +#endif + } + ~AutoCollectVsyncTelemetry() + { +#ifdef EARLY_BETA_OR_EARLIER + if (Telemetry::CanRecordBase()) { + CollectTelemetry(); + } +#endif + } + +private: + void CollectTelemetry(); + + bool mIsBackground; + nsCString mKey; + TimeStamp mStart; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +void +AutoCollectVsyncTelemetry::CollectTelemetry() +{ + TimeStamp now = TimeStamp::Now(); + + mozilla::Telemetry::HistogramID eventsId = + mIsBackground ? Telemetry::CONTENT_JS_BACKGROUND_TICK_DELAY_EVENTS_MS + : Telemetry::CONTENT_JS_FOREGROUND_TICK_DELAY_EVENTS_MS; + mozilla::Telemetry::HistogramID totalId = + mIsBackground ? Telemetry::CONTENT_JS_BACKGROUND_TICK_DELAY_TOTAL_MS + : Telemetry::CONTENT_JS_FOREGROUND_TICK_DELAY_TOTAL_MS; + + uint64_t lastSeenVsync = gEarliestUnprocessedVsync; + if (!lastSeenVsync) { + return; + } + + bool inconsistent = false; + TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent); + if (inconsistent) { + return; + } + + TimeStamp pendingVsync = + creation + TimeDuration::FromMicroseconds(lastSeenVsync); + + if (pendingVsync > now) { + return; + } + + uint32_t duration = + static_cast((now - pendingVsync).ToMilliseconds()); + + Telemetry::Accumulate(eventsId, mKey, duration); + Telemetry::Accumulate(totalId, duration); + + if (pendingVsync > mStart) { + return; + } + + Telemetry::Accumulate(Telemetry::CONTENT_JS_KNOWN_TICK_DELAY_MS, duration); + + return; +} + } // namespace NS_IMPL_ISUPPORTS(SchedulerEventTarget, SchedulerEventTarget, nsIEventTarget) @@ -126,6 +208,31 @@ SchedulerGroup::UnlabeledDispatch(const char* aName, } } +/* static */ void +SchedulerGroup::MarkVsyncReceived() +{ + if (gEarliestUnprocessedVsync) { + // If we've seen a vsync already, but haven't handled it, keep the + // older one. + return; + } + + MOZ_ASSERT(!NS_IsMainThread()); + bool inconsistent = false; + TimeStamp creation = TimeStamp::ProcessCreation(&inconsistent); + if (inconsistent) { + return; + } + + gEarliestUnprocessedVsync = (TimeStamp::Now() - creation).ToMicroseconds(); +} + +/* static */ void +SchedulerGroup::MarkVsyncRan() +{ + gEarliestUnprocessedVsync = 0; +} + SchedulerGroup* SchedulerGroup::sRunningDispatcher; SchedulerGroup::SchedulerGroup() @@ -256,7 +363,12 @@ SchedulerGroup::Runnable::Run() mDispatcher->SetValidatingAccess(StartValidation); - nsresult result = mRunnable->Run(); + nsresult result; + + { + AutoCollectVsyncTelemetry telemetry(this); + result = mRunnable->Run(); + } // The runnable's destructor can have side effects, so try to execute it in // the scope of the TabGroup. diff --git a/xpcom/threads/SchedulerGroup.h b/xpcom/threads/SchedulerGroup.h index 632f0eab2e0de..841956babf962 100644 --- a/xpcom/threads/SchedulerGroup.h +++ b/xpcom/threads/SchedulerGroup.h @@ -9,6 +9,7 @@ #include "mozilla/AlreadyAddRefed.h" #include "mozilla/TaskCategory.h" +#include "mozilla/TimeStamp.h" #include "nsCOMPtr.h" #include "nsISupportsImpl.h" @@ -38,6 +39,10 @@ class SchedulerGroup NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + // This method returns true if all members of the "group" are in a + // "background" state. + virtual bool IsBackground() const { return false; } + class MOZ_STACK_CLASS AutoProcessEvent final { public: AutoProcessEvent(); @@ -76,6 +81,10 @@ class SchedulerGroup TaskCategory aCategory, already_AddRefed&& aRunnable); + static void MarkVsyncReceived(); + + static void MarkVsyncRan(); + protected: // Implementations are guaranteed that this method is called on the main // thread. From d47d48053e2c5c1ea53d5bebecef4cc0953386f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:04:14 -0700 Subject: [PATCH 071/131] Bug 1321789 - Support Unicode extensions with multiple value subtags in BCP47 language tags. r=Waldo --- js/src/builtin/Intl.cpp | 79 +++---- js/src/builtin/Intl.js | 198 ++++++++++++------ .../Intl/DateTimeFormat/calendar-aliases.js | 35 ++++ js/src/tests/Intl/DateTimeFormat/islamic.js | 89 ++++++++ 4 files changed, 300 insertions(+), 101 deletions(-) create mode 100644 js/src/tests/Intl/DateTimeFormat/calendar-aliases.js create mode 100644 js/src/tests/Intl/DateTimeFormat/islamic.js diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 60795cc6e6e61..380e758325775 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -83,8 +83,8 @@ using mozilla::RangedPtr; * bit rot. The following stub implementations for ICU functions make this * possible. The functions using them should never be called, so they assert * and return error codes. Signatures adapted from ICU header files locid.h, - * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h; see the ICU - * directory for license. + * numsys.h, ucal.h, ucol.h, udat.h, udatpg.h, uenum.h, unum.h, uloc.h; + * see the ICU directory for license. */ namespace { @@ -831,6 +831,12 @@ u_strToUpper(UChar* dest, int32_t destCapacity, const UChar* src, int32_t srcLen MOZ_CRASH("u_strToUpper: Intl API disabled"); } +const char* +uloc_toUnicodeLocaleType(const char* keyword, const char* value) +{ + MOZ_CRASH("uloc_toUnicodeLocaleType: Intl API disabled"); +} + } // anonymous namespace #endif @@ -887,11 +893,8 @@ LegacyIntlInitialize(JSContext* cx, HandleObject obj, Handle init // CountAvailable and GetAvailable describe the signatures used for ICU API // to determine available locales for various functionality. -typedef int32_t -(* CountAvailable)(); - -typedef const char* -(* GetAvailable)(int32_t localeIndex); +using CountAvailable = int32_t (*)(); +using GetAvailable = const char* (*)(int32_t localeIndex); static bool intl_availableLocales(JSContext* cx, CountAvailable countAvailable, @@ -902,6 +905,7 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable, return false; #if ENABLE_INTL_API + RootedAtom a(cx); uint32_t count = countAvailable(); RootedValue t(cx, BooleanValue(true)); for (uint32_t i = 0; i < count; i++) { @@ -912,7 +916,7 @@ intl_availableLocales(JSContext* cx, CountAvailable countAvailable, char* p; while ((p = strchr(lang.get(), '_'))) *p = '-'; - RootedAtom a(cx, Atomize(cx, lang.get(), strlen(lang.get()))); + a = Atomize(cx, lang.get(), strlen(lang.get())); if (!a) return false; if (!DefineProperty(cx, locales, a->asPropertyName(), t, nullptr, nullptr, @@ -1211,6 +1215,8 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp) if (!DefineElement(cx, collations, index++, NullHandleValue)) return false; + RootedString jscollation(cx); + RootedValue element(cx); for (uint32_t i = 0; i < count; i++) { const char* collation = uenum_next(values, nullptr, &status); if (U_FAILURE(status)) { @@ -1225,21 +1231,11 @@ js::intl_availableCollations(JSContext* cx, unsigned argc, Value* vp) if (equal(collation, "standard") || equal(collation, "search")) continue; - // ICU returns old-style keyword values; map them to BCP 47 equivalents - // (see http://bugs.icu-project.org/trac/ticket/9620). - if (equal(collation, "dictionary")) - collation = "dict"; - else if (equal(collation, "gb2312han")) - collation = "gb2312"; - else if (equal(collation, "phonebook")) - collation = "phonebk"; - else if (equal(collation, "traditional")) - collation = "trad"; - - RootedString jscollation(cx, JS_NewStringCopyZ(cx, collation)); + // ICU returns old-style keyword values; map them to BCP 47 equivalents. + jscollation = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("co", collation)); if (!jscollation) return false; - RootedValue element(cx, StringValue(jscollation)); + element = StringValue(jscollation); if (!DefineElement(cx, collations, index++, element)) return false; } @@ -2678,19 +2674,16 @@ js::intl_DateTimeFormat_availableLocales(JSContext* cx, unsigned argc, Value* vp return true; } -// ICU returns old-style keyword values; map them to BCP 47 equivalents -// (see http://bugs.icu-project.org/trac/ticket/9620). -static const char* -bcp47CalendarName(const char* icuName) +struct CalendarAlias { - if (equal(icuName, "ethiopic-amete-alem")) - return "ethioaa"; - if (equal(icuName, "gregorian")) - return "gregory"; - if (equal(icuName, "islamic-civil")) - return "islamicc"; - return icuName; -} + const char* const calendar; + const char* const alias; +}; + +const CalendarAlias calendarAliases[] = { + { "islamic-civil", "islamicc" }, + { "ethioaa", "ethiopic-amete-alem" } +}; bool js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) @@ -2723,7 +2716,8 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) return false; } - jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar)); + // ICU returns old-style keyword values; map them to BCP 47 equivalents + jscalendar = JS_NewStringCopyZ(cx, uloc_toUnicodeLocaleType("ca", calendar)); if (!jscalendar) return false; } @@ -2753,12 +2747,27 @@ js::intl_availableCalendars(JSContext* cx, unsigned argc, Value* vp) return false; } - jscalendar = JS_NewStringCopyZ(cx, bcp47CalendarName(calendar)); + // ICU returns old-style keyword values; map them to BCP 47 equivalents + calendar = uloc_toUnicodeLocaleType("ca", calendar); + + jscalendar = JS_NewStringCopyZ(cx, calendar); if (!jscalendar) return false; element = StringValue(jscalendar); if (!DefineElement(cx, calendars, index++, element)) return false; + + // ICU doesn't return calendar aliases, append them here. + for (const auto& calendarAlias : calendarAliases) { + if (equal(calendar, calendarAlias.calendar)) { + jscalendar = JS_NewStringCopyZ(cx, calendarAlias.alias); + if (!jscalendar) + return false; + element = StringValue(jscalendar); + if (!DefineElement(cx, calendars, index++, element)) + return false; + } + } } args.rval().setObject(*calendars); diff --git a/js/src/builtin/Intl.js b/js/src/builtin/Intl.js index 5276f4ce0f317..ae96c077673d5 100644 --- a/js/src/builtin/Intl.js +++ b/js/src/builtin/Intl.js @@ -382,7 +382,7 @@ function CanonicalizeLanguageTag(locale) { if (hasOwn(locale, langTagMappings)) return langTagMappings[locale]; - var subtags = StringSplitString(ToString(locale), "-"); + var subtags = StringSplitString(locale, "-"); var i = 0; // Handle the standard part: All subtags before the first singleton or "x". @@ -930,10 +930,7 @@ function LookupMatcher(availableLocales, requestedLocales) { if (locale !== noExtensionsLocale) { var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE(); var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, locale); - var extension = extensionMatch[0]; - var extensionIndex = extensionMatch.index; - result.extension = extension; - result.extensionIndex = extensionIndex; + result.extension = extensionMatch[0]; } } else { result.locale = DefaultLocale(); @@ -957,6 +954,79 @@ function BestFitMatcher(availableLocales, requestedLocales) { } +/** + * Returns the Unicode extension value subtags for the requested key subtag. + * + * NOTE: PR to add UnicodeExtensionValue to ECMA-402 isn't yet written. + */ +function UnicodeExtensionValue(extension, key) { + assert(typeof extension === "string", "extension is a string value"); + assert(function() { + var unicodeLocaleExtensionSequenceRE = getUnicodeLocaleExtensionSequenceRE(); + var extensionMatch = regexp_exec_no_statics(unicodeLocaleExtensionSequenceRE, extension); + return extensionMatch !== null && extensionMatch[0] === extension; + }(), "extension is a Unicode extension subtag"); + assert(typeof key === "string", "key is a string value"); + assert(key.length === 2, "key is a Unicode extension key subtag"); + + // Step 1. + var size = extension.length; + + // Step 2. + var searchValue = "-" + key + "-"; + + // Step 3. + var pos = callFunction(std_String_indexOf, extension, searchValue); + + // Step 4. + if (pos !== -1) { + // Step 4.a. + var start = pos + 4; + + // Step 4.b. + var end = start; + + // Step 4.c. + var k = start; + + // Steps 4.d-e. + while (true) { + // Step 4.e.i. + var e = callFunction(std_String_indexOf, extension, "-", k); + + // Step 4.e.ii. + var len = e === -1 ? size - k : e - k; + + // Step 4.e.iii. + if (len === 2) + break; + + // Step 4.e.iv. + if (e === -1) { + end = size; + break; + } + + // Step 4.e.v. + end = e; + k = e + 1; + } + + // Step 4.f. + return callFunction(String_substring, extension, start, end); + } + + // Step 5. + searchValue = "-" + key; + + // Steps 6-7. + if (callFunction(std_String_endsWith, extension, searchValue)) + return ""; + + // Step 8 (implicit). +} + + /** * Compares a BCP 47 language priority list against availableLocales and * determines the best available language to meet the request. Options specified @@ -978,19 +1048,8 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte // Step 4. var foundLocale = r.locale; - // Step 5.a. + // Step 5 (Not applicable in this implementation). var extension = r.extension; - var extensionIndex, extensionSubtags, extensionSubtagsLength; - - // Step 5. - if (extension !== undefined) { - // Step 5.b. - extensionIndex = r.extensionIndex; - - // Steps 5.d-e. - extensionSubtags = StringSplitString(ToString(extension), "-"); - extensionSubtagsLength = extensionSubtags.length; - } // Steps 6-7. var result = new Record(); @@ -999,68 +1058,57 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte // Step 8. var supportedExtension = "-u"; - // Steps 9-11. + // Steps 9-12. var i = 0; var len = relevantExtensionKeys.length; var foundLocaleData; if (len > 0) { // In this implementation, localeData is a function, not an object. - // Step 11.b. + // Step 12.b. foundLocaleData = localeData(foundLocale); } while (i < len) { - // Step 11.a. + // Step 12.a. var key = relevantExtensionKeys[i]; - // Step 11.c. + // Step 12.c. var keyLocaleData = foundLocaleData[key]; // Locale data provides default value. - // Step 11.d. + // Step 12.d. var value = keyLocaleData[0]; + assert(typeof value === "string" || value === null, "unexpected locale data value"); // Locale tag may override. - // Step 11.e. + // Step 12.e. var supportedExtensionAddition = ""; - // Step 11.f is implemented by Utilities.js. - - var valuePos; - - // Step 11.g. - if (extensionSubtags !== undefined) { - // Step 11.g.i. - var keyPos = callFunction(ArrayIndexOf, extensionSubtags, key); - - // Step 11.g.ii. - if (keyPos !== -1) { - // Step 11.g.ii.1. - if (keyPos + 1 < extensionSubtagsLength && - extensionSubtags[keyPos + 1].length > 2) - { - // Step 11.g.ii.1.a. - var requestedValue = extensionSubtags[keyPos + 1]; - - // Step 11.g.ii.1.b. - valuePos = callFunction(ArrayIndexOf, keyLocaleData, requestedValue); - - // Step 11.g.ii.1.c. - if (valuePos !== -1) { + // Step 12.f. + if (extension !== undefined) { + // NB: The step annotations don't yet match the ES2017 Intl draft, + // 94045d234762ad107a3d09bb6f7381a65f1a2f9b, because the PR to add + // the new UnicodeExtensionValue abstract operation still needs to + // be written. + + // Step 12.f.i. + var requestedValue = UnicodeExtensionValue(extension, key); + + // Step 12.f.ii. + if (requestedValue !== undefined) { + // Step 12.f.ii.1. + if (requestedValue !== "") { + // Step 12.f.ii.1.a. + if (callFunction(ArrayIndexOf, keyLocaleData, requestedValue) !== -1) { value = requestedValue; supportedExtensionAddition = "-" + key + "-" + value; } } else { - // Step 11.g.ii.2. + // Step 12.f.ii.2. // According to the LDML spec, if there's no type value, // and true is an allowed value, it's used. - - // Step 11.g.ii.2.a. - valuePos = callFunction(ArrayIndexOf, keyLocaleData, "true"); - - // Step 11.g.ii.2.b. - if (valuePos !== -1) + if (callFunction(ArrayIndexOf, keyLocaleData, "true") !== -1) value = "true"; } } @@ -1068,35 +1116,53 @@ function ResolveLocale(availableLocales, requestedLocales, options, relevantExte // Options override all. - // Step 11.h.i. + // Step 12.g.i. var optionsValue = options[key]; - // Step 11.h, 11.h.ii. + // Step 12.g, 12.g.ii. if (optionsValue !== undefined && + optionsValue !== value && callFunction(ArrayIndexOf, keyLocaleData, optionsValue) !== -1) { - // Step 11.h.ii.1. - if (optionsValue !== value) { - value = optionsValue; - supportedExtensionAddition = ""; - } + value = optionsValue; + supportedExtensionAddition = ""; } - // Steps 11.i-k. + // Steps 12.h-j. result[key] = value; supportedExtension += supportedExtensionAddition; i++; } - // Step 12. + // Step 13. if (supportedExtension.length > 2) { - var preExtension = callFunction(String_substring, foundLocale, 0, extensionIndex); - var postExtension = callFunction(String_substring, foundLocale, extensionIndex); - foundLocale = preExtension + supportedExtension + postExtension; + assert(!callFunction(std_String_startsWith, foundLocale, "x-"), + "unexpected privateuse-only locale returned from ICU"); + + // Step 13.a. + var privateIndex = callFunction(std_String_indexOf, foundLocale, "-x-"); + + // Steps 13.b-c. + if (privateIndex === -1) { + foundLocale += supportedExtension; + } else { + var preExtension = callFunction(String_substring, foundLocale, 0, privateIndex); + var postExtension = callFunction(String_substring, foundLocale, privateIndex); + foundLocale = preExtension + supportedExtension + postExtension; + } + + // Step 13.d. + assert(IsStructurallyValidLanguageTag(foundLocale), "invalid locale after concatenation"); + + // Step 13.e (Not required in this implementation, because we don't + // canonicalize Unicode extension subtags). + assert(foundLocale === CanonicalizeLanguageTag(foundLocale), "same locale with extension"); } - // Steps 13-14. + // Step 14. result.locale = foundLocale; + + // Step 15. return result; } diff --git a/js/src/tests/Intl/DateTimeFormat/calendar-aliases.js b/js/src/tests/Intl/DateTimeFormat/calendar-aliases.js new file mode 100644 index 0000000000000..901adcb45f4e0 --- /dev/null +++ b/js/src/tests/Intl/DateTimeFormat/calendar-aliases.js @@ -0,0 +1,35 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +// Ensure ethiopic-amete-alem is resolved to ethioaa instead of ethiopic. +function testEthiopicAmeteAlem() { + var locale = "am-ET-u-nu-latn"; + var opts = {timeZone: "Africa/Addis_Ababa"}; + var dtfEthiopicAmeteAlem = new Intl.DateTimeFormat(`${locale}-ca-ethiopic-amete-alem`, opts); + var dtfEthioaa = new Intl.DateTimeFormat(`${locale}-ca-ethioaa`, opts); + var dtfEthiopic = new Intl.DateTimeFormat(`${locale}-ca-ethiopic`, opts); + + var date = new Date(2016, 1 - 1, 1); + + assertEq(dtfEthiopicAmeteAlem.format(date), dtfEthioaa.format(date)); + assertEq(dtfEthiopicAmeteAlem.format(date) === dtfEthiopic.format(date), false); +} + +// Ensure islamicc is resolved to islamic-civil. +function testIslamicCivil() { + var locale = "ar-SA-u-nu-latn"; + var opts = {timeZone: "Asia/Riyadh"}; + var dtfIslamicCivil = new Intl.DateTimeFormat(`${locale}-ca-islamic-civil`, opts); + var dtfIslamicc = new Intl.DateTimeFormat(`${locale}-ca-islamicc`, opts); + var dtfIslamic = new Intl.DateTimeFormat(`${locale}-ca-islamic`, opts); + + var date = new Date(2016, 1 - 1, 1); + + assertEq(dtfIslamicCivil.format(date), dtfIslamicc.format(date)); + assertEq(dtfIslamicCivil.format(date) === dtfIslamic.format(date), false); +} + +testEthiopicAmeteAlem(); +testIslamicCivil(); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); diff --git a/js/src/tests/Intl/DateTimeFormat/islamic.js b/js/src/tests/Intl/DateTimeFormat/islamic.js new file mode 100644 index 0000000000000..3a88590ea5e52 --- /dev/null +++ b/js/src/tests/Intl/DateTimeFormat/islamic.js @@ -0,0 +1,89 @@ +// |reftest| skip-if(!this.hasOwnProperty("Intl")) + +function civilDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-civil-nu-latn", opts).format(date); +} + +function tabularDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-tbla-nu-latn", opts).format(date); +} + +function sightingDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-islamic-rgsa-nu-latn", opts).format(date); +} + +function ummAlQuraDate(options, date) { + var opts = Object.assign({timeZone: "Asia/Riyadh"}, options); + return new Intl.DateTimeFormat("ar-SA-u-ca-umalqura-nu-latn", opts).format(date); +} + +// Test islamic-tbla (Tabular / Thursday epoch). +// Compare with islamic-civil (Tabular / Friday epoch). +function testIslamicTbla() { + var date = new Date(Date.UTC(2015, 1 - 1, 1)); + + // Month and year are the same. + var monthYear = {year: "numeric", month: "numeric"}; + assertEq(civilDate(monthYear, date), tabularDate(monthYear, date)); + + // Day is different by one. + var day = {day: "numeric"}; + assertEq(Number(civilDate(day, date)) - Number(tabularDate(day, date)), -1); +} + +// Test islamic-rgsa (Saudi Arabia sighting). +// Sighting of the hilal (crescent moon) in Saudi Arabia. +function testIslamicRgsa() { + var date1 = new Date(Date.UTC(1975, 5 - 1, 6)); + var date2 = new Date(Date.UTC(2015, 1 - 1, 1)); + var dayMonthYear = {year: "numeric", month: "numeric", day: "numeric"}; + + assertEq(sightingDate(dayMonthYear, date1), tabularDate(dayMonthYear, date1)); + assertEq(sightingDate(dayMonthYear, date2), civilDate(dayMonthYear, date2)); +} + +// Test islamic-umalqura (Umm al-Qura). +function testIslamicUmalqura() { + var year = {year: "numeric"}; + var month = {month: "numeric"}; + var day = {day: "numeric"}; + + // From ICU test files, which in turn was generated from: + // Official Umm-al-Qura calendar of SA: + // home, http://www.ummulqura.org.sa/default.aspx + // converter, http://www.ummulqura.org.sa/Index.aspx + var dates = [ + [ {year: 2016, month: 1, day: 11}, {year: 1437, month: 4, day: 1} ], + [ {year: 2016, month: 2, day: 10}, {year: 1437, month: 5, day: 1} ], + [ {year: 2016, month: 3, day: 10}, {year: 1437, month: 6, day: 1} ], + [ {year: 2016, month: 4, day: 8}, {year: 1437, month: 7, day: 1} ], + [ {year: 2016, month: 5, day: 8}, {year: 1437, month: 8, day: 1} ], + [ {year: 2016, month: 6, day: 6}, {year: 1437, month: 9, day: 1} ], + [ {year: 2016, month: 7, day: 6}, {year: 1437, month: 10, day: 1} ], + [ {year: 2016, month: 8, day: 4}, {year: 1437, month: 11, day: 1} ], + [ {year: 2016, month: 9, day: 2}, {year: 1437, month: 12, day: 1} ], + [ {year: 2016, month: 10, day: 2}, {year: 1438, month: 1, day: 1} ], + [ {year: 2016, month: 11, day: 1}, {year: 1438, month: 2, day: 1} ], + [ {year: 2016, month: 11, day: 30}, {year: 1438, month: 3, day: 1} ], + [ {year: 2016, month: 12, day: 30}, {year: 1438, month: 4, day: 1} ], + ]; + + for (var [gregorian, ummAlQura] of dates) { + var date = new Date(Date.UTC(gregorian.year, gregorian.month - 1, gregorian.day)); + + // Use parseInt() to remove the trailing era indicator. + assertEq(parseInt(ummAlQuraDate(year, date), 10), ummAlQura.year); + assertEq(Number(ummAlQuraDate(month, date)), ummAlQura.month); + assertEq(Number(ummAlQuraDate(day, date)), ummAlQura.day); + } +} + +testIslamicTbla(); +testIslamicRgsa(); +testIslamicUmalqura(); + +if (typeof reportCompare === "function") + reportCompare(0, 0, "ok"); From a745564f094681dcd6b540bff189edc553e1c57d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:22 -0700 Subject: [PATCH 072/131] Bug 1339395 - Part 1: Align parse method for object literals to match array literals. r=shu --- js/src/frontend/FullParseHandler.h | 5 +- js/src/frontend/Parser.cpp | 373 +++++++++++++++-------------- 2 files changed, 196 insertions(+), 182 deletions(-) diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index 2af40386232a1..db4c3fdf198c4 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -277,10 +277,9 @@ class FullParseHandlerBase } MOZ_MUST_USE bool addSpreadElement(ParseNode* literal, uint32_t begin, ParseNode* inner) { - TokenPos pos(begin, inner->pn_pos.end); - ParseNode* spread = new_(PNK_SPREAD, JSOP_NOP, pos, inner); + ParseNode* spread = newSpread(begin, inner); if (!spread) - return null(); + return false; literal->append(spread); literal->pn_xflags |= PNX_ARRAYHOLESPREAD | PNX_NONCONST; return true; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 740bb369a1ea1..0a09c1b5c2153 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4407,85 +4407,90 @@ Parser::objectBindingPattern(DeclarationKind kind, RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) break; - TokenPos namePos = pos(); - - tokenStream.ungetToken(); - - PropertyType propType; - Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); - if (!propName) + if (tt == TOK_TRIPLEDOT) { + // TODO: rest-binding property + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); + } else { + TokenPos namePos = tokenStream.nextToken().pos; - if (propType == PropertyType::Normal) { - // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. - - if (!tokenStream.getToken(&tt, TokenStream::Operand)) + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) return null(); - Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); - if (!binding) - return null(); + if (propType == PropertyType::Normal) { + // Handle e.g., |var {p: x} = o| and |var {p: x=0} = o|. - bool hasInitializer; - if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) - return null(); + if (!tokenStream.getToken(&tt, TokenStream::Operand)) + return null(); - Node bindingExpr = hasInitializer - ? bindingInitializer(binding, kind, yieldHandling) - : binding; - if (!bindingExpr) - return null(); + Node binding = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!binding) + return null(); - if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) - return null(); - } else if (propType == PropertyType::Shorthand) { - // Handle e.g., |var {x, y} = o| as destructuring shorthand - // for |var {x: x, y: y} = o|. - MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + bool hasInitializer; + if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN)) + return null(); - Node binding = bindingIdentifier(kind, yieldHandling); - if (!binding) - return null(); + Node bindingExpr = hasInitializer + ? bindingInitializer(binding, kind, yieldHandling) + : binding; + if (!bindingExpr) + return null(); - if (!handler.addShorthand(literal, propName, binding)) - return null(); - } else if (propType == PropertyType::CoverInitializedName) { - // Handle e.g., |var {x=1, y=2} = o| as destructuring shorthand - // with default values. - MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else if (propType == PropertyType::Shorthand) { + // Handle e.g., |var {x, y} = o| as destructuring shorthand + // for |var {x: x, y: y} = o|. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); - Node binding = bindingIdentifier(kind, yieldHandling); - if (!binding) - return null(); + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); + + if (!handler.addShorthand(literal, propName, binding)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + // Handle e.g., |var {x=1, y=2} = o| as destructuring + // shorthand with default values. + MOZ_ASSERT(TokenKindIsPossibleIdentifierName(tt)); + + Node binding = bindingIdentifier(kind, yieldHandling); + if (!binding) + return null(); - tokenStream.consumeKnownToken(TOK_ASSIGN); + tokenStream.consumeKnownToken(TOK_ASSIGN); - Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); - if (!bindingExpr) - return null(); + Node bindingExpr = bindingInitializer(binding, kind, yieldHandling); + if (!bindingExpr) + return null(); - if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + if (!handler.addPropertyDefinition(literal, propName, bindingExpr)) + return null(); + } else { + errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); return null(); - } else { - errorAt(namePos.begin, JSMSG_NO_VARIABLE_NAME); - return null(); + } } - if (!tokenStream.getToken(&tt)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (tt == TOK_RC) + if (!matched) break; - if (tt != TOK_COMMA) { - reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, begin); - return null(); - } } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, begin)); + handler.setEndPosition(literal, pos().end); return literal; } @@ -9779,174 +9784,184 @@ Parser::objectLiteral(YieldHandling yieldHandling, RootedAtom propAtom(context); for (;;) { TokenKind tt; - if (!tokenStream.getToken(&tt)) + if (!tokenStream.peekToken(&tt)) return null(); if (tt == TOK_RC) break; - TokenPos namePos = pos(); - - tokenStream.ungetToken(); - - PropertyType propType; - Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); - if (!propName) + if (tt == TOK_TRIPLEDOT) { + // TODO: object spread + error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); return null(); + } else { + TokenPos namePos = tokenStream.nextToken().pos; - if (propType == PropertyType::Normal) { - TokenPos exprPos; - if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) + PropertyType propType; + Node propName = propertyName(yieldHandling, declKind, literal, &propType, &propAtom); + if (!propName) return null(); - Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, - possibleError); - if (!propExpr) - return null(); + if (propType == PropertyType::Normal) { + TokenPos exprPos; + if (!tokenStream.peekTokenPos(&exprPos, TokenStream::Operand)) + return null(); - handler.checkAndSetIsDirectRHSAnonFunction(propExpr); + Node propExpr = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!propExpr) + return null(); - if (possibleError) - checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); + handler.checkAndSetIsDirectRHSAnonFunction(propExpr); - if (foldConstants && !FoldConstants(context, &propExpr, this)) - return null(); + if (possibleError) + checkDestructuringAssignmentElement(propExpr, exprPos, possibleError); - if (propAtom == context->names().proto) { - if (seenPrototypeMutation) { - // Directly report the error when we're not in a - // destructuring context. - if (!possibleError) { - errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); - return null(); + if (foldConstants && !FoldConstants(context, &propExpr, this)) + return null(); + + if (propAtom == context->names().proto) { + if (seenPrototypeMutation) { + // Directly report the error when we're not in a + // destructuring context. + if (!possibleError) { + errorAt(namePos.begin, JSMSG_DUPLICATE_PROTO_PROPERTY); + return null(); + } + + // Otherwise delay error reporting until we've + // determined whether or not we're destructuring. + possibleError->setPendingExpressionErrorAt(namePos, + JSMSG_DUPLICATE_PROTO_PROPERTY); } + seenPrototypeMutation = true; - // Otherwise delay error reporting until we've determined - // whether or not we're destructuring. - possibleError->setPendingExpressionErrorAt(namePos, - JSMSG_DUPLICATE_PROTO_PROPERTY); - } - seenPrototypeMutation = true; + // Note: this occurs *only* if we observe TOK_COLON! Only + // __proto__: v mutates [[Prototype]]. Getters, setters, + // method/generator definitions, computed property name + // versions of all of these, and shorthands do not. + if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) + return null(); + } else { + if (!handler.isConstant(propExpr)) + handler.setListFlag(literal, PNX_NONCONST); - // Note: this occurs *only* if we observe TOK_COLON! Only - // __proto__: v mutates [[Prototype]]. Getters, setters, - // method/generator definitions, computed property name - // versions of all of these, and shorthands do not. - if (!handler.addPrototypeMutation(literal, namePos.begin, propExpr)) + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } + } else if (propType == PropertyType::Shorthand) { + /* + * Support, e.g., |({x, y} = o)| as destructuring shorthand + * for |({x: x, y: y} = o)|, and |var o = {x, y}| as + * initializer shorthand for |var o = {x: x, y: y}|. + */ + Rooted name(context, identifierReference(yieldHandling)); + if (!name) return null(); - } else { - if (!handler.isConstant(propExpr)) - handler.setListFlag(literal, PNX_NONCONST); - if (!handler.addPropertyDefinition(literal, propName, propExpr)) + Node nameExpr = identifierReference(name); + if (!nameExpr) return null(); - } - } else if (propType == PropertyType::Shorthand) { - /* - * Support, e.g., |({x, y} = o)| as destructuring shorthand - * for |({x: x, y: y} = o)|, and |var o = {x, y}| as initializer - * shorthand for |var o = {x: x, y: y}|. - */ - Rooted name(context, identifierReference(yieldHandling)); - if (!name) - return null(); - Node nameExpr = identifierReference(name); - if (!nameExpr) - return null(); + if (possibleError) + checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); - if (possibleError) - checkDestructuringAssignmentTarget(nameExpr, namePos, possibleError); + if (!handler.addShorthand(literal, propName, nameExpr)) + return null(); + } else if (propType == PropertyType::CoverInitializedName) { + /* + * Support, e.g., |({x=1, y=2} = o)| as destructuring + * shorthand with default values, as per ES6 12.14.5 + */ + Rooted name(context, identifierReference(yieldHandling)); + if (!name) + return null(); - if (!handler.addShorthand(literal, propName, nameExpr)) - return null(); - } else if (propType == PropertyType::CoverInitializedName) { - /* - * Support, e.g., |({x=1, y=2} = o)| as destructuring shorthand - * with default values, as per ES6 12.14.5 - */ - Rooted name(context, identifierReference(yieldHandling)); - if (!name) - return null(); + Node lhs = identifierReference(name); + if (!lhs) + return null(); - Node lhs = identifierReference(name); - if (!lhs) - return null(); + tokenStream.consumeKnownToken(TOK_ASSIGN); - tokenStream.consumeKnownToken(TOK_ASSIGN); + if (!seenCoverInitializedName) { + // "shorthand default" or "CoverInitializedName" syntax is + // only valid in the case of destructuring. + seenCoverInitializedName = true; - if (!seenCoverInitializedName) { - // "shorthand default" or "CoverInitializedName" syntax is only - // valid in the case of destructuring. - seenCoverInitializedName = true; + if (!possibleError) { + // Destructuring defaults are definitely not allowed + // in this object literal, because of something the + // caller knows about the preceding code. For example, + // maybe the preceding token is an operator: + // |x + {y=z}|. + error(JSMSG_COLON_AFTER_ID); + return null(); + } - if (!possibleError) { - // Destructuring defaults are definitely not allowed in this object literal, - // because of something the caller knows about the preceding code. - // For example, maybe the preceding token is an operator: `x + {y=z}`. - error(JSMSG_COLON_AFTER_ID); - return null(); + // Here we set a pending error so that later in the parse, + // once we've determined whether or not we're + // destructuring, the error can be reported or ignored + // appropriately. + possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); } - // Here we set a pending error so that later in the parse, once we've - // determined whether or not we're destructuring, the error can be - // reported or ignored appropriately. - possibleError->setPendingExpressionErrorAt(pos(), JSMSG_COLON_AFTER_ID); - } + if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { + // |chars| is "arguments" or "eval" here. + if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + return null(); + } - if (const char* chars = handler.nameIsArgumentsEvalAnyParentheses(lhs, context)) { - // |chars| is "arguments" or "eval" here. - if (!strictModeErrorAt(namePos.begin, JSMSG_BAD_STRICT_ASSIGN, chars)) + Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); + if (!rhs) return null(); - } - - Node rhs = assignExpr(InAllowed, yieldHandling, TripledotProhibited); - if (!rhs) - return null(); - handler.checkAndSetIsDirectRHSAnonFunction(rhs); + handler.checkAndSetIsDirectRHSAnonFunction(rhs); - Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); - if (!propExpr) - return null(); + Node propExpr = handler.newAssignment(PNK_ASSIGN, lhs, rhs, JSOP_NOP); + if (!propExpr) + return null(); - if (!handler.addPropertyDefinition(literal, propName, propExpr)) - return null(); - } else { - RootedAtom funName(context); - if (!tokenStream.isCurrentTokenType(TOK_RB)) { - funName = propAtom; + if (!handler.addPropertyDefinition(literal, propName, propExpr)) + return null(); + } else { + RootedAtom funName(context); + if (!tokenStream.isCurrentTokenType(TOK_RB)) { + funName = propAtom; - if (propType == PropertyType::Getter || propType == PropertyType::Setter) { - funName = prefixAccessorName(propType, propAtom); - if (!funName) - return null(); + if (propType == PropertyType::Getter || propType == PropertyType::Setter) { + funName = prefixAccessorName(propType, propAtom); + if (!funName) + return null(); + } } - } - Node fn = methodDefinition(namePos.begin, propType, funName); - if (!fn) - return null(); + Node fn = methodDefinition(namePos.begin, propType, funName); + if (!fn) + return null(); - handler.checkAndSetIsDirectRHSAnonFunction(fn); + handler.checkAndSetIsDirectRHSAnonFunction(fn); - JSOp op = JSOpFromPropertyType(propType); - if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) - return null(); + JSOp op = JSOpFromPropertyType(propType); + if (!handler.addObjectMethodDefinition(literal, propName, fn, op)) + return null(); - if (possibleError) - possibleError->setPendingDestructuringErrorAt(namePos, JSMSG_BAD_DESTRUCT_TARGET); + if (possibleError) { + possibleError->setPendingDestructuringErrorAt(namePos, + JSMSG_BAD_DESTRUCT_TARGET); + } + } } - if (!tokenStream.getToken(&tt)) + bool matched; + if (!tokenStream.matchToken(&matched, TOK_COMMA)) return null(); - if (tt == TOK_RC) + if (!matched) break; - if (tt != TOK_COMMA) { - reportMissingClosing(JSMSG_CURLY_AFTER_LIST, JSMSG_CURLY_OPENED, openedPos); - return null(); - } } + MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, + reportMissingClosing(JSMSG_CURLY_AFTER_LIST, + JSMSG_CURLY_OPENED, openedPos)); + handler.setEndPosition(literal, pos().end); return literal; } From f276a7617345dfe3748c0e10cdc4d0b0c5b96532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:26 -0700 Subject: [PATCH 073/131] Bug 1339395 - Part 2: Add parser support for rest and spread object properties. r=shu --- js/src/builtin/ReflectParse.cpp | 12 +++++++++ js/src/frontend/BytecodeEmitter.cpp | 12 +++++++++ js/src/frontend/FullParseHandler.h | 12 +++++++++ js/src/frontend/Parser.cpp | 40 +++++++++++++++++++++++----- js/src/frontend/SyntaxParseHandler.h | 1 + 5 files changed, 71 insertions(+), 6 deletions(-) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index a7b5ec5fcea27..3e278bbaba7ef 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -3226,6 +3226,8 @@ ASTSerializer::property(ParseNode* pn, MutableHandleValue dst) return expression(pn->pn_kid, &val) && builder.prototypeMutation(val, &pn->pn_pos, dst); } + if (pn->isKind(PNK_SPREAD)) + return expression(pn, dst); PropKind kind; switch (pn->getOp()) { @@ -3346,6 +3348,16 @@ ASTSerializer::objectPattern(ParseNode* pn, MutableHandleValue dst) return false; for (ParseNode* propdef = pn->pn_head; propdef; propdef = propdef->pn_next) { + if (propdef->isKind(PNK_SPREAD)) { + RootedValue target(cx); + RootedValue spread(cx); + if (!pattern(propdef->pn_kid, &target)) + return false; + if(!builder.spreadExpression(target, &propdef->pn_pos, &spread)) + return false; + elts.infallibleAppend(spread); + continue; + } LOCAL_ASSERT(propdef->isKind(PNK_MUTATEPROTO) != propdef->isOp(JSOP_INITPROP)); RootedValue key(cx); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f1f32b1a24df6..c2faa2f437df9 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5892,6 +5892,11 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla return false; for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { + if (member->isKind(PNK_SPREAD)) { + // FIXME: Implement + continue; + } + ParseNode* subpattern; if (member->isKind(PNK_MUTATEPROTO)) subpattern = member->pn_kid; @@ -9720,6 +9725,13 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, continue; } + if (propdef->isKind(PNK_SPREAD)) { + MOZ_ASSERT(type == ObjectLiteral); + objp.set(nullptr); + // FIXME: implement + continue; + } + bool extraPop = false; if (type == ClassBody && propdef->as().isStatic()) { extraPop = true; diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h index db4c3fdf198c4..ceea05e939847 100644 --- a/js/src/frontend/FullParseHandler.h +++ b/js/src/frontend/FullParseHandler.h @@ -370,6 +370,18 @@ class FullParseHandlerBase return true; } + MOZ_MUST_USE bool addSpreadProperty(ParseNode* literal, uint32_t begin, ParseNode* inner) { + MOZ_ASSERT(literal->isKind(PNK_OBJECT)); + MOZ_ASSERT(literal->isArity(PN_LIST)); + + setListFlag(literal, PNX_NONCONST); + ParseNode* spread = newSpread(begin, inner); + if (!spread) + return false; + literal->append(spread); + return true; + } + MOZ_MUST_USE bool addObjectMethodDefinition(ParseNode* literal, ParseNode* key, ParseNode* fn, JSOp op) { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0a09c1b5c2153..61d7e13b4f716 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -4413,9 +4413,19 @@ Parser::objectBindingPattern(DeclarationKind kind, break; if (tt == TOK_TRIPLEDOT) { - // TODO: rest-binding property - error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); - return null(); + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; + + TokenKind tt; + if (!tokenStream.getToken(&tt)) + return null(); + + Node inner = bindingIdentifierOrPattern(kind, yieldHandling, tt); + if (!inner) + return null(); + + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); } else { TokenPos namePos = tokenStream.nextToken().pos; @@ -4485,6 +4495,10 @@ Parser::objectBindingPattern(DeclarationKind kind, return null(); if (!matched) break; + if (tt == TOK_TRIPLEDOT) { + error(JSMSG_REST_WITH_COMMA); + return null(); + } } MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, @@ -9790,9 +9804,21 @@ Parser::objectLiteral(YieldHandling yieldHandling, break; if (tt == TOK_TRIPLEDOT) { - // TODO: object spread - error(JSMSG_UNEXPECTED_TOKEN, "property name", TokenKindToDesc(tt)); - return null(); + tokenStream.consumeKnownToken(TOK_TRIPLEDOT); + uint32_t begin = pos().begin; + + TokenPos innerPos; + if (!tokenStream.peekTokenPos(&innerPos, TokenStream::Operand)) + return null(); + + Node inner = assignExpr(InAllowed, yieldHandling, TripledotProhibited, + possibleError); + if (!inner) + return null(); + if (possibleError) + checkDestructuringAssignmentTarget(inner, innerPos, possibleError); + if (!handler.addSpreadProperty(literal, begin, inner)) + return null(); } else { TokenPos namePos = tokenStream.nextToken().pos; @@ -9956,6 +9982,8 @@ Parser::objectLiteral(YieldHandling yieldHandling, return null(); if (!matched) break; + if (tt == TOK_TRIPLEDOT && possibleError) + possibleError->setPendingDestructuringErrorAt(pos(), JSMSG_REST_WITH_COMMA); } MUST_MATCH_TOKEN_MOD_WITH_REPORT(TOK_RC, TokenStream::None, diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index 424755a4d7c7c..975f184acf691 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -295,6 +295,7 @@ class SyntaxParseHandlerBase MOZ_MUST_USE bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; } MOZ_MUST_USE bool addPropertyDefinition(Node literal, Node name, Node expr) { return true; } MOZ_MUST_USE bool addShorthand(Node literal, Node name, Node expr) { return true; } + MOZ_MUST_USE bool addSpreadProperty(Node literal, uint32_t begin, Node inner) { return true; } MOZ_MUST_USE bool addObjectMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; } MOZ_MUST_USE bool addClassMethodDefinition(Node literal, Node name, Node fn, JSOp op, bool isStatic) { return true; } Node newYieldExpression(uint32_t begin, Node value) { return NodeGeneric; } From bf189dab61f2ac663ea928975f95e7a2638b615e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:29 -0700 Subject: [PATCH 074/131] Bug 1339395 - Part 3: Add BytecodeEmitter support for object rest and spread properties. r=shu --- js/src/builtin/Utilities.js | 63 ++++++ js/src/frontend/BytecodeEmitter.cpp | 304 ++++++++++++++++++++++++---- js/src/frontend/BytecodeEmitter.h | 15 ++ js/src/vm/CommonPropertyNames.h | 2 + 4 files changed, 339 insertions(+), 45 deletions(-) diff --git a/js/src/builtin/Utilities.js b/js/src/builtin/Utilities.js index 3ae3cddbf46da..db6c67463e409 100644 --- a/js/src/builtin/Utilities.js +++ b/js/src/builtin/Utilities.js @@ -236,6 +236,69 @@ function GetInternalError(msg) { // To be used when a function is required but calling it shouldn't do anything. function NullFunction() {} +// Object Rest/Spread Properties proposal +// Abstract operation: CopyDataProperties (target, source, excluded) +function CopyDataProperties(target, source, excluded) { + // Step 1. + assert(IsObject(target), "target is an object"); + + // Step 2. + assert(IsObject(excluded), "excluded is an object"); + + // Steps 3, 6. + if (source === undefined || source === null) + return; + + // Step 4.a. + source = ToObject(source); + + // Step 4.b. + var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); + + // Step 5. + for (var index = 0; index < keys.length; index++) { + var key = keys[index]; + + // We abbreviate this by calling propertyIsEnumerable which is faster + // and returns false for not defined properties. + if (!hasOwn(key, excluded) && callFunction(std_Object_propertyIsEnumerable, source, key)) + _DefineDataProperty(target, key, source[key]); + } + + // Step 6 (Return). +} + +// Object Rest/Spread Properties proposal +// Abstract operation: CopyDataProperties (target, source, excluded) +function CopyDataPropertiesUnfiltered(target, source) { + // Step 1. + assert(IsObject(target), "target is an object"); + + // Step 2 (Not applicable). + + // Steps 3, 6. + if (source === undefined || source === null) + return; + + // Step 4.a. + source = ToObject(source); + + // Step 4.b. + var keys = OwnPropertyKeys(source, JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS); + + // Step 5. + for (var index = 0; index < keys.length; index++) { + var key = keys[index]; + + // We abbreviate this by calling propertyIsEnumerable which is faster + // and returns false for not defined properties. + if (callFunction(std_Object_propertyIsEnumerable, source, key)) + _DefineDataProperty(target, key, source[key]); + } + + // Step 6 (Return). +} + /*************************************** Testing functions ***************************************/ function outer() { return function inner() { diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index c2faa2f437df9..169761d9bd9f6 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5891,41 +5891,78 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla if (!emitRequireObjectCoercible()) // ... RHS return false; - for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { - if (member->isKind(PNK_SPREAD)) { - // FIXME: Implement - continue; - } + bool needsRestPropertyExcludedSet = pattern->pn_count > 1 && + pattern->last()->isKind(PNK_SPREAD); + if (needsRestPropertyExcludedSet) { + if (!emitDestructuringObjRestExclusionSet(pattern)) // ... RHS SET + return false; + if (!emit1(JSOP_SWAP)) // ... SET RHS + return false; + } + + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { ParseNode* subpattern; - if (member->isKind(PNK_MUTATEPROTO)) + if (member->isKind(PNK_MUTATEPROTO) || member->isKind(PNK_SPREAD)) subpattern = member->pn_kid; else subpattern = member->pn_right; + ParseNode* lhs = subpattern; + MOZ_ASSERT_IF(member->isKind(PNK_SPREAD), !lhs->isKind(PNK_ASSIGN)); if (lhs->isKind(PNK_ASSIGN)) lhs = lhs->pn_left; size_t emitted; - if (!emitDestructuringLHSRef(lhs, &emitted)) // ... RHS *LREF + if (!emitDestructuringLHSRef(lhs, &emitted)) // ... *SET RHS *LREF return false; // Duplicate the value being destructured to use as a reference base. if (emitted) { - if (!emitDupAt(emitted)) // ... RHS *LREF RHS + if (!emitDupAt(emitted)) // ... *SET RHS *LREF RHS return false; } else { - if (!emit1(JSOP_DUP)) // ... RHS RHS + if (!emit1(JSOP_DUP)) // ... *SET RHS RHS return false; } + if (member->isKind(PNK_SPREAD)) { + if (!updateSourceCoordNotes(member->pn_pos.begin)) + return false; + + if (!emitNewInit(JSProto_Object)) // ... *SET RHS *LREF RHS TARGET + return false; + if (!emit1(JSOP_DUP)) // ... *SET RHS *LREF RHS TARGET TARGET + return false; + if (!emit2(JSOP_PICK, 2)) // ... *SET RHS *LREF TARGET TARGET RHS + return false; + + if (needsRestPropertyExcludedSet) { + if (!emit2(JSOP_PICK, emitted + 4)) // ... RHS *LREF TARGET TARGET RHS SET + return false; + } + + CopyOption option = needsRestPropertyExcludedSet + ? CopyOption::Filtered + : CopyOption::Unfiltered; + if (!emitCopyDataProperties(option)) // ... RHS *LREF TARGET + return false; + + // Destructure TARGET per this member's lhs. + if (!emitSetOrInitializeDestructuring(lhs, flav)) // ... RHS + return false; + + MOZ_ASSERT(member == pattern->last(), "Rest property is always last"); + break; + } + // Now push the property name currently being matched, which is the // current property name "label" on the left of a colon in the object // initialiser. bool needsGetElem = true; if (member->isKind(PNK_MUTATEPROTO)) { - if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitAtomOp(cx->names().proto, JSOP_GETPROP)) // ... *SET RHS *LREF PROP return false; needsGetElem = false; } else { @@ -5933,7 +5970,7 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla ParseNode* key = member->pn_left; if (key->isKind(PNK_NUMBER)) { - if (!emitNumberOp(key->pn_dval)) // ... RHS *LREF RHS KEY + if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { PropertyName* name = key->pn_atom->asPropertyName(); @@ -5943,30 +5980,142 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla // as indexes for simplification of downstream analysis. jsid id = NameToId(name); if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... RHS *LREF RHS KEY + if (!emitTree(key)) // ... *SET RHS *LREF RHS KEY return false; } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ... RHS *LREF PROP + if (!emitAtomOp(name, JSOP_GETPROP)) // ... *SET RHS *LREF PROP return false; needsGetElem = false; } } else { - if (!emitComputedPropertyName(key)) // ... RHS *LREF RHS KEY + if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY return false; + + // Add the computed property key to the exclusion set. + if (needsRestPropertyExcludedSet) { + if (!emitDupAt(emitted + 3)) // ... SET RHS *LREF RHS KEY SET + return false; + if (!emitDupAt(1)) // ... SET RHS *LREF RHS KEY SET KEY + return false; + if (!emit1(JSOP_UNDEFINED)) // ... SET RHS *LREF RHS KEY SET KEY UNDEFINED + return false; + if (!emit1(JSOP_INITELEM)) // ... SET RHS *LREF RHS KEY SET + return false; + if (!emit1(JSOP_POP)) // ... SET RHS *LREF RHS KEY + return false; + } } } // Get the property value if not done already. - if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... RHS *LREF PROP + if (needsGetElem && !emitElemOpBase(JSOP_GETELEM)) // ... *SET RHS *LREF PROP return false; if (subpattern->isKind(PNK_ASSIGN)) { - if (!emitDefault(subpattern->pn_right, lhs)) // ... RHS *LREF VALUE + if (!emitDefault(subpattern->pn_right, lhs)) // ... *SET RHS *LREF VALUE return false; } // Destructure PROP per this member's lhs. - if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... RHS + if (!emitSetOrInitializeDestructuring(subpattern, flav)) // ... *SET RHS + return false; + } + + return true; +} + +bool +BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern) +{ + MOZ_ASSERT(pattern->isKind(PNK_OBJECT)); + MOZ_ASSERT(pattern->isArity(PN_LIST)); + MOZ_ASSERT(pattern->last()->isKind(PNK_SPREAD)); + + ptrdiff_t offset = this->offset(); + if (!emitNewInit(JSProto_Object)) + return false; + + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. + gc::AllocKind kind = gc::GetGCObjectKind(pattern->pn_count - 1); + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, kind, TenuredObject)); + if (!obj) + return false; + + RootedAtom pnatom(cx); + for (ParseNode* member = pattern->pn_head; member; member = member->pn_next) { + if (member->isKind(PNK_SPREAD)) + break; + + bool isIndex = false; + if (member->isKind(PNK_MUTATEPROTO)) { + pnatom.set(cx->names().proto); + } else { + ParseNode* key = member->pn_left; + if (key->isKind(PNK_NUMBER)) { + if (!emitNumberOp(key->pn_dval)) + return false; + isIndex = true; + } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { + // The parser already checked for atoms representing indexes and + // used PNK_NUMBER instead, but also watch for ids which TI treats + // as indexes for simplification of downstream analysis. + jsid id = NameToId(key->pn_atom->asPropertyName()); + if (id != IdToTypeId(id)) { + if (!emitTree(key)) + return false; + isIndex = true; + } else { + pnatom.set(key->pn_atom); + } + } else { + // Otherwise this is a computed property name which needs to + // be added dynamically. + obj.set(nullptr); + continue; + } + } + + // Initialize elements with |undefined|. + if (!emit1(JSOP_UNDEFINED)) + return false; + + if (isIndex) { + obj.set(nullptr); + if (!emit1(JSOP_INITELEM)) + return false; + } else { + uint32_t index; + if (!makeAtomIndex(pnatom, &index)) + return false; + + if (obj) { + MOZ_ASSERT(!obj->inDictionaryMode()); + Rooted id(cx, AtomToId(pnatom)); + if (!NativeDefineProperty(cx, obj, id, UndefinedHandleValue, nullptr, nullptr, + JSPROP_ENUMERATE)) + { + return false; + } + if (obj->inDictionaryMode()) + obj.set(nullptr); + } + + if (!emitIndex32(JSOP_INITPROP, index)) + return false; + } + } + + if (obj) { + // The object survived and has a predictable shape: update the + // original bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; } @@ -6836,6 +6985,53 @@ BytecodeEmitter::emitRequireObjectCoercible() return true; } +bool +BytecodeEmitter::emitCopyDataProperties(CopyOption option) +{ + DebugOnly depth = this->stackDepth; + + uint32_t argc; + if (option == CopyOption::Filtered) { + MOZ_ASSERT(depth > 2); // TARGET SOURCE SET + argc = 3; + + if (!emitAtomOp(cx->names().CopyDataProperties, + JSOP_GETINTRINSIC)) // TARGET SOURCE SET COPYDATAPROPERTIES + { + return false; + } + } else { + MOZ_ASSERT(depth > 1); // TARGET SOURCE + argc = 2; + + if (!emitAtomOp(cx->names().CopyDataPropertiesUnfiltered, + JSOP_GETINTRINSIC)) // TARGET SOURCE COPYDATAPROPERTIES + { + return false; + } + } + + if (!emit1(JSOP_UNDEFINED)) // TARGET SOURCE *SET COPYDATAPROPERTIES UNDEFINED + return false; + if (!emit2(JSOP_PICK, argc + 1)) // SOURCE *SET COPYDATAPROPERTIES UNDEFINED TARGET + return false; + if (!emit2(JSOP_PICK, argc + 1)) // *SET COPYDATAPROPERTIES UNDEFINED TARGET SOURCE + return false; + if (option == CopyOption::Filtered) { + if (!emit2(JSOP_PICK, argc + 1)) // COPYDATAPROPERTIES UNDEFINED TARGET SOURCE SET + return false; + } + if (!emitCall(JSOP_CALL_IGNORES_RV, argc)) // IGNORED + return false; + checkTypeSet(JSOP_CALL_IGNORES_RV); + + if (!emit1(JSOP_POP)) // - + return false; + + MOZ_ASSERT(depth - int(argc) == this->stackDepth); + return true; +} + bool BytecodeEmitter::emitIterator() { @@ -9727,8 +9923,17 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, if (propdef->isKind(PNK_SPREAD)) { MOZ_ASSERT(type == ObjectLiteral); + + if (!emit1(JSOP_DUP)) + return false; + + if (!emitTree(propdef->pn_kid)) + return false; + + if (!emitCopyDataProperties(CopyOption::Unfiltered)) + return false; + objp.set(nullptr); - // FIXME: implement continue; } @@ -9758,7 +9963,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, // The parser already checked for atoms representing indexes and // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simpliciation of downstream analysis. + // as indexes for simplification of downstream analysis. jsid id = NameToId(key->pn_atom->asPropertyName()); if (id != IdToTypeId(id)) { if (!emitTree(key)) @@ -9845,8 +10050,7 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, MOZ_ASSERT(!IsHiddenInitOp(op)); MOZ_ASSERT(!objp->inDictionaryMode()); Rooted id(cx, AtomToId(key->pn_atom)); - RootedValue undefinedValue(cx, UndefinedValue()); - if (!NativeDefineProperty(cx, objp, id, undefinedValue, nullptr, nullptr, + if (!NativeDefineProperty(cx, objp, id, UndefinedHandleValue, nullptr, nullptr, JSPROP_ENUMERATE)) { return false; @@ -9889,15 +10093,16 @@ BytecodeEmitter::emitObject(ParseNode* pn) if (!emitNewInit(JSProto_Object)) return false; - /* - * Try to construct the shape of the object as we go, so we can emit a - * JSOP_NEWOBJECT with the final shape instead. - */ - RootedPlainObject obj(cx); - // No need to do any guessing for the object kind, since we know exactly - // how many properties we plan to have. + // Try to construct the shape of the object as we go, so we can emit a + // JSOP_NEWOBJECT with the final shape instead. + // In the case of computed property names and indices, we cannot fix the + // shape at bytecode compile time. When the shape cannot be determined, + // |obj| is nulled out. + + // No need to do any guessing for the object kind, since we know the upper + // bound of how many properties we plan to have. gc::AllocKind kind = gc::GetGCObjectKind(pn->pn_count); - obj = NewBuiltinClassInstance(cx, kind, TenuredObject); + RootedPlainObject obj(cx, NewBuiltinClassInstance(cx, kind, TenuredObject)); if (!obj) return false; @@ -9905,25 +10110,34 @@ BytecodeEmitter::emitObject(ParseNode* pn) return false; if (obj) { - /* - * The object survived and has a predictable shape: update the original - * bytecode. - */ - ObjectBox* objbox = parser.newObjectBox(obj); - if (!objbox) + // The object survived and has a predictable shape: update the original + // bytecode. + if (!replaceNewInitWithNewObject(obj, offset)) return false; + } - static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, - "newinit and newobject must have equal length to edit in-place"); + return true; +} - uint32_t index = objectList.add(objbox); - jsbytecode* code = this->code(offset); - code[0] = JSOP_NEWOBJECT; - code[1] = jsbytecode(index >> 24); - code[2] = jsbytecode(index >> 16); - code[3] = jsbytecode(index >> 8); - code[4] = jsbytecode(index); - } +bool +BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset) +{ + ObjectBox* objbox = parser.newObjectBox(obj); + if (!objbox) + return false; + + static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, + "newinit and newobject must have equal length to edit in-place"); + + uint32_t index = objectList.add(objbox); + jsbytecode* code = this->code(offset); + + MOZ_ASSERT(code[0] == JSOP_NEWINIT); + code[0] = JSOP_NEWOBJECT; + code[1] = jsbytecode(index >> 24); + code[2] = jsbytecode(index >> 16); + code[3] = jsbytecode(index >> 8); + code[4] = jsbytecode(index); return true; } diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index b1449004fb896..4f60bf8b05505 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -553,6 +553,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(ParseNode* pn, bool needsProto = false); MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ParseNode* pn); + MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset); + MOZ_MUST_USE bool emitHoistedFunctionsInList(ParseNode* pn); MOZ_MUST_USE bool emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, @@ -689,6 +691,10 @@ struct MOZ_STACK_CLASS BytecodeEmitter // []/{} expression). MOZ_MUST_USE bool emitSetOrInitializeDestructuring(ParseNode* target, DestructuringFlavor flav); + // emitDestructuringObjRestExclusionSet emits the property exclusion set + // for the rest-property in an object pattern. + MOZ_MUST_USE bool emitDestructuringObjRestExclusionSet(ParseNode* pattern); + // emitDestructuringOps assumes the to-be-destructured value has been // pushed on the stack and emits code to destructure each part of a [] or // {} lhs expression. @@ -706,6 +712,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter // object, with no overall effect on the stack. MOZ_MUST_USE bool emitRequireObjectCoercible(); + enum class CopyOption { + Filtered, Unfiltered + }; + + // Calls either the |CopyDataProperties| or the + // |CopyDataPropertiesUnfiltered| intrinsic function, consumes three (or + // two in the latter case) elements from the stack. + MOZ_MUST_USE bool emitCopyDataProperties(CopyOption option); + // emitIterator expects the iterable to already be on the stack. // It will replace that stack value with the corresponding iterator MOZ_MUST_USE bool emitIterator(); diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 8471175ff1a78..6f96c2eeaa325 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -74,6 +74,8 @@ macro(constructor, constructor, "constructor") \ macro(continue, continue_, "continue") \ macro(ConvertAndCopyTo, ConvertAndCopyTo, "ConvertAndCopyTo") \ + macro(CopyDataProperties, CopyDataProperties, "CopyDataProperties") \ + macro(CopyDataPropertiesUnfiltered, CopyDataPropertiesUnfiltered, "CopyDataPropertiesUnfiltered") \ macro(copyWithin, copyWithin, "copyWithin") \ macro(count, count, "count") \ macro(CreateResolvingFunctions, CreateResolvingFunctions, "CreateResolvingFunctions") \ From c0a633b08d2919bdc1a322b4969f63d435cf3804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:33 -0700 Subject: [PATCH 075/131] Bug 1339395 - Part 4: Enable test262 for object rest and spread properties. r=shu --- js/src/tests/jstests.list | 283 -------------------------------------- 1 file changed, 283 deletions(-) diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 3d2b364712e30..4ebcf8b65ccd2 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -519,289 +519,6 @@ skip script test262/language/expressions/prefix-decrement/target-cover-newtarget skip script test262/language/expressions/prefix-decrement/non-simple.js skip script test262/language/asi/S7.9_A5.7_T1.js -# https://bugzilla.mozilla.org/show_bug.cgi?id=1339395 -skip script test262/language/statements/let/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/let/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/let/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/let/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/let/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/let/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/variable/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-getter.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/generators/dstr-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/generators/yield-identifier-spread-non-strict.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/generators/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/generators/yield-spread-obj.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-getter.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/function/dstr-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/function/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-getter.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-getter.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/for/dstr-const-obj-ptrn-rest-getter.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for/dstr-var-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/for/dstr-let-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/const/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for-of/dstr-obj-rest-val-null.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-getter.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for-of/dstr-obj-rest-nested-obj.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/for-of/dstr-obj-rest-nested-obj-nested-rest.js -skip script test262/language/statements/for-of/dstr-obj-rest-to-property-with-setter.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for-of/dstr-obj-rest-put-const.js -skip script test262/language/statements/for-of/dstr-obj-rest-str-val.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for-of/dstr-obj-rest-empty-obj.js -skip script test262/language/statements/for-of/dstr-obj-rest-obj-own-property.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for-of/dstr-obj-rest-number.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/for-of/dstr-obj-rest-to-property.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/for-of/dstr-obj-rest-val-undefined.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-getter.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/for-of/dstr-const-obj-ptrn-rest-getter.js -skip script test262/language/statements/for-of/dstr-obj-rest-getter-abrupt-get-error.js -skip script test262/language/statements/for-of/dstr-obj-rest-symbol-val.js -skip script test262/language/statements/for-of/dstr-obj-rest-descriptors.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/for-of/dstr-obj-rest-valid-object.js -skip script test262/language/statements/for-of/dstr-var-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/for-of/dstr-obj-rest-skip-non-enumerable.js -skip script test262/language/statements/for-of/dstr-obj-rest-getter.js -skip script test262/language/statements/for-of/dstr-let-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-getter.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/try/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-gen-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-getter.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/statements/class/dstr-gen-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/statements/class/dstr-meth-static-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/statements/class/dstr-gen-meth-static-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/statements/class/dstr-meth-static-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/statements/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-getter.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/arrow-function/dstr-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/arrow-function/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-getter.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/generators/dstr-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/generators/yield-identifier-spread-non-strict.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/generators/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/generators/yield-spread-obj.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-getter.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/function/dstr-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/function/dstr-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-getter.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/object/dstr-gen-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/object/dstr-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-getter.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/object/dstr-gen-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/object/dstr-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/object/method-definition/generator-yield-spread-obj.js -skip script test262/language/expressions/object/method-definition/generator-yield-identifier-spread-non-strict.js -skip script test262/language/expressions/assignment/dstr-obj-rest-val-null.js -skip script test262/language/expressions/assignment/dstr-obj-rest-nested-obj.js -skip script test262/language/expressions/assignment/dstr-obj-rest-nested-obj-nested-rest.js -skip script test262/language/expressions/assignment/dstr-obj-rest-to-property-with-setter.js -skip script test262/language/expressions/assignment/dstr-obj-rest-put-const.js -skip script test262/language/expressions/assignment/dstr-obj-rest-str-val.js -skip script test262/language/expressions/assignment/dstr-obj-rest-empty-obj.js -skip script test262/language/expressions/assignment/dstr-obj-rest-obj-own-property.js -skip script test262/language/expressions/assignment/dstr-obj-rest-number.js -skip script test262/language/expressions/assignment/dstr-obj-rest-to-property.js -skip script test262/language/expressions/assignment/dstr-obj-rest-val-undefined.js -skip script test262/language/expressions/assignment/dstr-obj-rest-getter-abrupt-get-error.js -skip script test262/language/expressions/assignment/dstr-obj-rest-symbol-val.js -skip script test262/language/expressions/assignment/dstr-obj-rest-descriptors.js -skip script test262/language/expressions/assignment/dstr-obj-rest-valid-object.js -skip script test262/language/expressions/assignment/dstr-obj-rest-skip-non-enumerable.js -skip script test262/language/expressions/assignment/dstr-obj-rest-getter.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-gen-meth-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-getter.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-nested-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-obj-own-property.js -skip script test262/language/expressions/class/dstr-meth-static-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-meth-dflt-obj-ptrn-rest-obj-nested-rest.js -skip script test262/language/expressions/class/dstr-gen-meth-static-obj-ptrn-rest-skip-non-enumerable.js -skip script test262/language/expressions/class/gen-method-yield-spread-obj.js -skip script test262/language/expressions/class/dstr-meth-static-dflt-obj-ptrn-rest-val-obj.js -skip script test262/language/expressions/class/dstr-gen-meth-static-dflt-obj-ptrn-rest-getter.js - # Dependent on evalInWorker, setSharedArrayBuffer, and # getSharedArrayBuffer, plus the test cases can't actually run in the # browser even if that were fixed, https://bugzil.la/1349863 From 3ceaf0df4268c34743d5b32eb124b527c05dfa1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:36 -0700 Subject: [PATCH 076/131] Bug 1339395 - Part 5: Add Reflect.parse tests for object rest and spread properties. r=shu --- .../js1_8_5/reflect-parse/object-rest.js | 45 +++++++++++++++++++ .../js1_8_5/reflect-parse/object-spread.js | 29 ++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 js/src/tests/js1_8_5/reflect-parse/object-rest.js create mode 100644 js/src/tests/js1_8_5/reflect-parse/object-spread.js diff --git a/js/src/tests/js1_8_5/reflect-parse/object-rest.js b/js/src/tests/js1_8_5/reflect-parse/object-rest.js new file mode 100644 index 0000000000000..5af06909bcef1 --- /dev/null +++ b/js/src/tests/js1_8_5/reflect-parse/object-rest.js @@ -0,0 +1,45 @@ +// |reftest| skip-if(!xulRuntime.shell) + +function property(key, value = key, shorthand = key === value) { + return { key, value, shorthand }; +} + +function assertDestrAssign(src, pattern) { + assertExpr(`(${src} = 0)`, aExpr("=", pattern, lit(0))); +} + +function assertDestrBinding(src, pattern) { + assertDecl(`var ${src} = 0`, varDecl([{id: pattern, init: lit(0)}])); +} + +function test() { + // Target expression must be a simple assignment target or a nested pattern + // in object assignment patterns. + assertDestrAssign("{...x}", objPatt([spread(ident("x"))])); + assertDestrAssign("{...(x)}", objPatt([spread(ident("x"))])); + assertDestrAssign("{...obj.p}", objPatt([spread(dotExpr(ident("obj"), ident("p")))])); + assertDestrAssign("{...{}}", objPatt([spread(objPatt([]))])); + assertDestrAssign("{...[]}", objPatt([spread(arrPatt([]))])); + + // Object binding patterns only allow binding identifiers or nested patterns. + assertDestrBinding("{...x}", objPatt([spread(ident("x"))])); + assertDestrBinding("{...{}}", objPatt([spread(objPatt([]))])); + assertDestrBinding("{...[]}", objPatt([spread(arrPatt([]))])); + + // The rest-property can be preceded by other properties. + for (var assertDestr of [assertDestrAssign, assertDestrBinding]) { + assertDestr("{a, ...x}", objPatt([property(ident("a")), spread(ident("x"))])); + assertDestr("{a: b, ...x}", objPatt([property(ident("a"), ident("b")), spread(ident("x"))])); + assertDestr("{[a]: b, ...x}", objPatt([property(comp(ident("a")), ident("b")), spread(ident("x"))])); + } + + // Tests when __proto__ is used in the object pattern. + for (var assertDestr of [assertDestrAssign, assertDestrBinding]) { + assertDestr("{...__proto__}", objPatt([spread(ident("__proto__"))])); + assertDestr("{__proto__, ...x}", objPatt([property(ident("__proto__")), spread(ident("x"))])); + } + assertDestrAssign("{__proto__: a, ...x}", objPatt([property(lit("__proto__"), ident("a")), spread(ident("x"))])); + assertDestrBinding("{__proto__: a, ...x}", objPatt([property(ident("__proto__"), ident("a")), spread(ident("x"))])); +} + +runtest(test); diff --git a/js/src/tests/js1_8_5/reflect-parse/object-spread.js b/js/src/tests/js1_8_5/reflect-parse/object-spread.js new file mode 100644 index 0000000000000..a4b269c409868 --- /dev/null +++ b/js/src/tests/js1_8_5/reflect-parse/object-spread.js @@ -0,0 +1,29 @@ +// |reftest| skip-if(!xulRuntime.shell) + +function property(key, value = key, shorthand = key === value) { + return { key, value, shorthand }; +} + +function test() { + // Any expression can be spreaded. + assertExpr("({...x})", objExpr([spread(ident("x"))])); + assertExpr("({...f()})", objExpr([spread(callExpr(ident("f"), []))])); + assertExpr("({...123})", objExpr([spread(lit(123))])); + + // Multiple spread expression are allowed. + assertExpr("({...x, ...obj.p})", objExpr([spread(ident("x")), spread(dotExpr(ident("obj"), ident("p")))])); + + // Spread expression can appear anywhere in an object literal. + assertExpr("({p, ...x})", objExpr([property(ident("p")), spread(ident("x"))])); + assertExpr("({p: a, ...x})", objExpr([property(ident("p"), ident("a")), spread(ident("x"))])); + assertExpr("({...x, p: a})", objExpr([spread(ident("x")), property(ident("p"), ident("a"))])); + + // Trailing comma after spread expression is allowed. + assertExpr("({...x,})", objExpr([spread(ident("x"))])); + + // __proto__ is not special in spread expressions. + assertExpr("({...__proto__})", objExpr([spread(ident("__proto__"))])); + assertExpr("({...__proto__, ...__proto__})", objExpr([spread(ident("__proto__")), spread(ident("__proto__"))])); +} + +runtest(test); From 751b1739457a4511c1894df2c1945bdacf9fe967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:39 -0700 Subject: [PATCH 077/131] Bug 1339395 - Part 6: Update jit-tests now that object rest/spread properties are a thing. r=shu --- js/src/jit-test/tests/parser/arrow-rest.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/src/jit-test/tests/parser/arrow-rest.js b/js/src/jit-test/tests/parser/arrow-rest.js index 53750f25bedba..b1429066efe93 100644 --- a/js/src/jit-test/tests/parser/arrow-rest.js +++ b/js/src/jit-test/tests/parser/arrow-rest.js @@ -39,7 +39,7 @@ testThrow(` testThrow(` ({...a)=> -`, 2); +`, 6); testThrow(` function f([... ...a)=> @@ -47,7 +47,7 @@ function f([... ...a)=> testThrow(` function f({...a)=> -`, 12); +`, 16); // arrow @@ -67,7 +67,7 @@ var [... ...a)=> testThrow(` var {...a)=> -`, 5); +`, 9); // initializer From dd2875fb2bb1d1116f5bc33fd6ac3dc11196f8af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:42 -0700 Subject: [PATCH 078/131] Bug 1339395 - Part 7: Remove no longer needed check for jsid strings which are indices from frontend. r=shu --- js/src/frontend/BytecodeEmitter.cpp | 39 +++-------------------------- js/src/frontend/FoldConstants.cpp | 7 ------ 2 files changed, 4 insertions(+), 42 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 169761d9bd9f6..b18c20c3cb123 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5973,20 +5973,9 @@ BytecodeEmitter::emitDestructuringOpsObject(ParseNode* pattern, DestructuringFla if (!emitNumberOp(key->pn_dval)) // ... *SET RHS *LREF RHS KEY return false; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { - PropertyName* name = key->pn_atom->asPropertyName(); - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(name); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) // ... *SET RHS *LREF RHS KEY - return false; - } else { - if (!emitAtomOp(name, JSOP_GETPROP)) // ... *SET RHS *LREF PROP - return false; - needsGetElem = false; - } + if (!emitAtomOp(key->pn_atom, JSOP_GETPROP)) // ... *SET RHS *LREF PROP + return false; + needsGetElem = false; } else { if (!emitComputedPropertyName(key)) // ... *SET RHS *LREF RHS KEY return false; @@ -6063,17 +6052,7 @@ BytecodeEmitter::emitDestructuringObjRestExclusionSet(ParseNode* pattern) return false; isIndex = true; } else if (key->isKind(PNK_OBJECT_PROPERTY_NAME) || key->isKind(PNK_STRING)) { - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(key->pn_atom->asPropertyName()); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) - return false; - isIndex = true; - } else { - pnatom.set(key->pn_atom); - } + pnatom.set(key->pn_atom); } else { // Otherwise this is a computed property name which needs to // be added dynamically. @@ -9960,16 +9939,6 @@ BytecodeEmitter::emitPropertyList(ParseNode* pn, MutableHandlePlainObject objp, { continue; } - - // The parser already checked for atoms representing indexes and - // used PNK_NUMBER instead, but also watch for ids which TI treats - // as indexes for simplification of downstream analysis. - jsid id = NameToId(key->pn_atom->asPropertyName()); - if (id != IdToTypeId(id)) { - if (!emitTree(key)) - return false; - isIndex = true; - } } else { if (!emitComputedPropertyName(key)) return false; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 1dfed75ce7629..c90e83f1043a8 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -1348,15 +1348,8 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, Parserpn_pos.end); if (!dottedAccess) return false; From 2f96d2684f6dc7d3b3e73bebc469bbd46c00376b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Thu, 4 May 2017 05:05:45 -0700 Subject: [PATCH 079/131] Bug 1339395 - Part 8: Add separate variables for wasm debugger test. r=luke --- js/src/jit-test/tests/debug/wasm-12.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jit-test/tests/debug/wasm-12.js b/js/src/jit-test/tests/debug/wasm-12.js index 85d43b094b310..18d3b574d8ab9 100644 --- a/js/src/jit-test/tests/debug/wasm-12.js +++ b/js/src/jit-test/tests/debug/wasm-12.js @@ -7,7 +7,7 @@ var g = newGlobal(); g.eval(` function initWasm(s) { return new WebAssembly.Instance(new WebAssembly.Module(wasmTextToBinary(s))); } o = initWasm('(module (func) (export "" 0))'); -o = initWasm('(module (func) (func) (export "" 1))'); +o2 = initWasm('(module (func) (func) (export "" 1))'); `); function isWasm(script) { return script.format === "wasm"; } From 7645a8f881665e2974dfc79011d97f5a68cfadd9 Mon Sep 17 00:00:00 2001 From: Michal Novotny Date: Thu, 4 May 2017 17:37:42 +0200 Subject: [PATCH 080/131] Bug 1361435 - Remove references to unused pref network.http.bypass-cachelock-threshold, r=mcmanus Removes references to now unused pref that was added in bug 868441 and removed in bugs 913807 and 1054572. It also removes some leftovers from http channel which have not been removed in those 2 bugs. --- addon-sdk/source/python-lib/cuddlefish/prefs.py | 2 -- addon-sdk/source/test/preferences/test.json | 1 - modules/libpref/init/all.js | 8 -------- netwerk/protocol/http/nsHttpHandler.h | 10 ---------- .../client/marionette_driver/geckoinstance.py | 2 -- testing/marionette/server.js | 3 --- testing/profiles/prefs_general.js | 3 --- 7 files changed, 29 deletions(-) diff --git a/addon-sdk/source/python-lib/cuddlefish/prefs.py b/addon-sdk/source/python-lib/cuddlefish/prefs.py index 9361338ecfaf4..031ace2759252 100644 --- a/addon-sdk/source/python-lib/cuddlefish/prefs.py +++ b/addon-sdk/source/python-lib/cuddlefish/prefs.py @@ -216,8 +216,6 @@ 'layout.css.report_errors': True, 'layout.css.grid.enabled': True, 'layout.spammy_warnings.enabled': False, - # Make sure the disk cache doesn't get auto disabled - 'network.http.bypass-cachelock-threshold': 200000, # Always use network provider for geolocation tests # so we bypass the OSX dialog raised by the corelocation provider 'geo.provider.testing': True, diff --git a/addon-sdk/source/test/preferences/test.json b/addon-sdk/source/test/preferences/test.json index 13da5da9b89d5..c0b49e24d5f0e 100644 --- a/addon-sdk/source/test/preferences/test.json +++ b/addon-sdk/source/test/preferences/test.json @@ -31,7 +31,6 @@ "layout.css.report_errors": true, "layout.css.grid.enabled": true, "layout.spammy_warnings.enabled": false, - "network.http.bypass-cachelock-threshold": 200000, "geo.provider.testing": true, "browser.pagethumbnails.capturing_disabled": true, "browser.download.panel.shown": true, diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index b643f876dba79..f7b772ebdbfbe 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1600,14 +1600,6 @@ pref("network.http.rendering-critical-requests-prioritization", true); // IPv6 connectivity. pref("network.http.fast-fallback-to-IPv4", true); -// The maximum amount of time the cache session lock can be held -// before a new transaction bypasses the cache. In milliseconds. -#ifdef RELEASE_OR_BETA -pref("network.http.bypass-cachelock-threshold", 200000); -#else -pref("network.http.bypass-cachelock-threshold", 250); -#endif - // Try and use SPDY when using SSL pref("network.http.spdy.enabled", true); pref("network.http.spdy.enabled.http2", true); diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 9237ab9ff76ac..ab6a6d7b4b140 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -353,12 +353,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler // returns true in between Init and Shutdown states bool Active() { return mHandlerActive; } - // When the disk cache is responding slowly its use is suppressed - // for 1 minute for most requests. Callable from main thread only. - TimeStamp GetCacheSkippedUntil() { return mCacheSkippedUntil; } - void SetCacheSkippedUntil(TimeStamp arg) { mCacheSkippedUntil = arg; } - void ClearCacheSkippedUntil() { mCacheSkippedUntil = TimeStamp(); } - nsIRequestContextService *GetRequestContextService() { return mRequestContextService.get(); @@ -560,10 +554,6 @@ class nsHttpHandler final : public nsIHttpProtocolHandler // while those elements load. bool mCriticalRequestPrioritization; - // When the disk cache is responding slowly its use is suppressed - // for 1 minute for most requests. - TimeStamp mCacheSkippedUntil; - // TCP Keepalive configuration values. // True if TCP keepalive is enabled for short-lived conns. diff --git a/testing/marionette/client/marionette_driver/geckoinstance.py b/testing/marionette/client/marionette_driver/geckoinstance.py index e19d8df6928ba..9224ba821a515 100644 --- a/testing/marionette/client/marionette_driver/geckoinstance.py +++ b/testing/marionette/client/marionette_driver/geckoinstance.py @@ -82,8 +82,6 @@ class GeckoInstance(object): "media.volume_scale": "0.01", - # Make sure the disk cache doesn't get auto disabled - "network.http.bypass-cachelock-threshold": 200000, # Do not prompt for temporary redirects "network.http.prompt-temp-redirect": False, # Disable speculative connections so they aren"t reported as leaking when they"re diff --git a/testing/marionette/server.js b/testing/marionette/server.js index 7a2fc65089fdc..8761cd08a18fe 100644 --- a/testing/marionette/server.js +++ b/testing/marionette/server.js @@ -222,9 +222,6 @@ const RECOMMENDED_PREFS = new Map([ // Show chrome errors and warnings in the error console ["javascript.options.showInConsole", true], - // Make sure the disk cache doesn't get auto disabled - ["network.http.bypass-cachelock-threshold", 200000], - // Do not prompt for temporary redirects ["network.http.prompt-temp-redirect", false], diff --git a/testing/profiles/prefs_general.js b/testing/profiles/prefs_general.js index 2537400afeca1..ffed5df59da69 100644 --- a/testing/profiles/prefs_general.js +++ b/testing/profiles/prefs_general.js @@ -198,9 +198,6 @@ user_pref("layout.spammy_warnings.enabled", false); user_pref("media.mediasource.mp4.enabled", true); user_pref("media.mediasource.webm.enabled", true); -// Make sure the disk cache doesn't get auto disabled -user_pref("network.http.bypass-cachelock-threshold", 200000); - // Enable Gamepad user_pref("dom.gamepad.enabled", true); user_pref("dom.gamepad.non_standard_events.enabled", true); From 09d8a10866911f877cd80a9c2bed839ada57a481 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 26 Apr 2017 14:15:30 +0200 Subject: [PATCH 081/131] Bug 1352506: Remove dead code in the backtracking allocator; r=luke --- js/src/jit/BacktrackingAllocator.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/js/src/jit/BacktrackingAllocator.cpp b/js/src/jit/BacktrackingAllocator.cpp index bf26b3293d6e4..11f5af5cb4567 100644 --- a/js/src/jit/BacktrackingAllocator.cpp +++ b/js/src/jit/BacktrackingAllocator.cpp @@ -590,18 +590,12 @@ BacktrackingAllocator::buildLivenessInfo() if (!callRanges.insert(callRange)) return false; } - DebugOnly hasDoubleDef = false; - DebugOnly hasFloat32Def = false; + for (size_t i = 0; i < ins->numDefs(); i++) { LDefinition* def = ins->getDef(i); if (def->isBogusTemp()) continue; -#ifdef DEBUG - if (def->type() == LDefinition::DOUBLE) - hasDoubleDef = true; - if (def->type() == LDefinition::FLOAT32) - hasFloat32Def = true; -#endif + CodePosition from = outputOf(*ins); if (def->policy() == LDefinition::MUST_REUSE_INPUT) { @@ -646,8 +640,7 @@ BacktrackingAllocator::buildLivenessInfo() } } - CodePosition to = - ins->isCall() ? outputOf(*ins) : outputOf(*ins).next(); + CodePosition to = ins->isCall() ? outputOf(*ins) : outputOf(*ins).next(); if (!vreg(temp).addInitialRange(alloc(), from, to)) return false; From 68db988c86f7eb0dc7b83a39ab916aca0b84b1bd Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 26 Apr 2017 14:40:37 +0200 Subject: [PATCH 082/131] Bug 1352506: Automatically setPerformsCall for LIR call instructions; r=sunfish --- js/src/jit-test/tests/asm.js/bug1357048.js | 21 +++++++++ js/src/jit/LIR.h | 12 +++++ js/src/jit/Lowering.cpp | 8 +--- js/src/jit/MIR.h | 4 ++ js/src/jit/arm/CodeGenerator-arm.cpp | 18 ++++++-- js/src/jit/arm/LIR-arm.h | 43 ++++------------- js/src/jit/arm/Lowering-arm.cpp | 54 ++++++++-------------- js/src/jit/shared/Lowering-shared-inl.h | 4 +- js/src/jit/x86/Lowering-x86.cpp | 8 ---- 9 files changed, 83 insertions(+), 89 deletions(-) create mode 100644 js/src/jit-test/tests/asm.js/bug1357048.js diff --git a/js/src/jit-test/tests/asm.js/bug1357048.js b/js/src/jit-test/tests/asm.js/bug1357048.js new file mode 100644 index 0000000000000..60a515d1fb17b --- /dev/null +++ b/js/src/jit-test/tests/asm.js/bug1357048.js @@ -0,0 +1,21 @@ +// |jit-test| +load(libdir + "asm.js"); + +function runTest() { + var code = USE_ASM + ` + function f() { + var x = 0; + var y = 0; + x = (((0x77777777 - 0xcccccccc) | 0) % -1) | 1; + y = (((0x7FFFFFFF + 0x7FFFFFFF) | 0) % -1) | 0; + return (x + y) | 0; + } + return f; + `; + + assertEq(asmLink(asmCompile(code))(), 1); +} + +runTest(); +setARMHwCapFlags('vfp'); +runTest(); diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 4300c5117e868..8d27bd3089885 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -1188,6 +1188,18 @@ class LCallInstructionHelper : public LInstructionHelper } }; +template +class LBinaryCallInstructionHelper : public LCallInstructionHelper +{ + public: + const LAllocation* lhs() { + return this->getOperand(0); + } + const LAllocation* rhs() { + return this->getOperand(1); + } +}; + class LRecoverInfo : public TempObject { public: diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 131a36d425088..3922582a2c1c0 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1919,8 +1919,6 @@ LIRGenerator::visitMod(MMod* ins) MOZ_ASSERT(ins->lhs()->type() == MIRType::Double); MOZ_ASSERT(ins->rhs()->type() == MIRType::Double); - gen->setPerformsCall(); - // Ion does an unaligned ABI call and thus needs a temp register. Wasm // doesn't. LDefinition maybeTemp = gen->compilingWasm() @@ -2266,13 +2264,13 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) break; case MIRType::Double: - // May call into JS::ToInt32(). + // May call into JS::ToInt32() on the slow OOL path. gen->setPerformsCall(); lowerTruncateDToInt32(truncate); break; case MIRType::Float32: - // May call into JS::ToInt32(). + // May call into JS::ToInt32() on the slow OOL path. gen->setPerformsCall(); lowerTruncateFToInt32(truncate); break; @@ -4470,8 +4468,6 @@ LIRGenerator::visitWasmStackArg(MWasmStackArg* ins) void LIRGenerator::visitWasmCall(MWasmCall* ins) { - gen->setPerformsCall(); - LAllocation* args = gen->allocate(ins->numOperands()); if (!args) { abort(AbortReason::Alloc, "Couldn't allocate for MWasmCall"); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 259f4dbc890b8..5ff0a99436449 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -7308,6 +7308,10 @@ class MMod : public MBinaryArithInstruction unsigned_ == ins->toMod()->isUnsigned(); } + bool possiblyCalls() const override { + return type() == MIRType::Double; + } + ALLOW_CLONE(MMod) }; diff --git a/js/src/jit/arm/CodeGenerator-arm.cpp b/js/src/jit/arm/CodeGenerator-arm.cpp index 86e81a9f7fc29..cdf9f1e5eaf22 100644 --- a/js/src/jit/arm/CodeGenerator-arm.cpp +++ b/js/src/jit/arm/CodeGenerator-arm.cpp @@ -781,7 +781,8 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins) Label done; // Save the lhs in case we end up with a 0 that should be a -0.0 because lhs < 0. - MOZ_ASSERT(callTemp.code() > r3.code() && callTemp.code() < r12.code()); + MOZ_ASSERT(callTemp != lhs); + MOZ_ASSERT(callTemp != rhs); masm.ma_mov(lhs, callTemp); // Prevent INT_MIN % -1; @@ -821,6 +822,9 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins) masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_idivmod)); } + MOZ_ASSERT(r1 != output); + masm.move32(r1, output); + // If X%Y == 0 and X < 0, then we *actually* wanted to return -0.0 if (mir->canBeNegativeDividend()) { if (mir->isTruncated()) { @@ -828,12 +832,13 @@ CodeGeneratorARM::visitSoftModI(LSoftModI* ins) } else { MOZ_ASSERT(mir->fallible()); // See if X < 0 - masm.as_cmp(r1, Imm8(0)); + masm.as_cmp(output, Imm8(0)); masm.ma_b(&done, Assembler::NotEqual); masm.as_cmp(callTemp, Imm8(0)); bailoutIf(Assembler::Signed, ins->snapshot()); } } + masm.bind(&done); } @@ -2825,9 +2830,7 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSoftUDivOrMod* ins) MOZ_ASSERT(lhs == r0); MOZ_ASSERT(rhs == r1); - MOZ_ASSERT(ins->mirRaw()->isDiv() || ins->mirRaw()->isMod()); - MOZ_ASSERT_IF(ins->mirRaw()->isDiv(), output == r0); - MOZ_ASSERT_IF(ins->mirRaw()->isMod(), output == r1); + MOZ_ASSERT(output == r0); Label done; MDiv* div = ins->mir()->isDiv() ? ins->mir()->toDiv() : nullptr; @@ -2849,6 +2852,11 @@ CodeGeneratorARM::visitSoftUDivOrMod(LSoftUDivOrMod* ins) masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, __aeabi_uidivmod)); } + if (mod) { + MOZ_ASSERT(output == r0, "output should not be r1 for mod"); + masm.move32(r1, output); + } + // uidivmod returns the quotient in r0, and the remainder in r1. if (div && !div->canTruncateRemainder()) { MOZ_ASSERT(div->fallible()); diff --git a/js/src/jit/arm/LIR-arm.h b/js/src/jit/arm/LIR-arm.h index 0b06dcf1ae99f..0a59ba24fb1d0 100644 --- a/js/src/jit/arm/LIR-arm.h +++ b/js/src/jit/arm/LIR-arm.h @@ -190,29 +190,15 @@ class LUDivOrModI64 : public LCallInstructionHelper +// divide instruction, implemented as a C++ native call. +class LSoftDivI : public LBinaryCallInstructionHelper<1, 0> { public: LIR_HEADER(SoftDivI); - LSoftDivI(const LAllocation& lhs, const LAllocation& rhs, - const LDefinition& temp1, const LDefinition& temp2, const LDefinition& temp3) { + LSoftDivI(const LAllocation& lhs, const LAllocation& rhs) { setOperand(0, lhs); setOperand(1, rhs); - setTemp(0, temp1); - setTemp(1, temp2); - setTemp(2, temp3); } MDiv* mir() const { @@ -268,25 +254,21 @@ class LModI : public LBinaryMath<1> } }; -class LSoftModI : public LBinaryMath<4> +class LSoftModI : public LBinaryCallInstructionHelper<1, 1> { public: LIR_HEADER(SoftModI); LSoftModI(const LAllocation& lhs, const LAllocation& rhs, - const LDefinition& temp1, const LDefinition& temp2, const LDefinition& temp3, - const LDefinition& callTemp) + const LDefinition& temp) { setOperand(0, lhs); setOperand(1, rhs); - setTemp(0, temp1); - setTemp(1, temp2); - setTemp(2, temp3); - setTemp(3, callTemp); + setTemp(0, temp); } const LDefinition* callTemp() { - return getTemp(3); + return getTemp(0); } MMod* mir() const { @@ -465,18 +447,14 @@ class LUMod : public LBinaryMath<0> } }; -class LSoftUDivOrMod : public LBinaryMath<3> +class LSoftUDivOrMod : public LBinaryCallInstructionHelper<1, 0> { public: LIR_HEADER(SoftUDivOrMod); - LSoftUDivOrMod(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp1, - const LDefinition& temp2, const LDefinition& temp3) { + LSoftUDivOrMod(const LAllocation& lhs, const LAllocation& rhs) { setOperand(0, lhs); setOperand(1, rhs); - setTemp(0, temp1); - setTemp(1, temp2); - setTemp(2, temp3); } MInstruction* mir() { @@ -581,8 +559,7 @@ class LWasmTruncateToInt64 : public LCallInstructionHelper public: LIR_HEADER(WasmTruncateToInt64); - LWasmTruncateToInt64(const LAllocation& in) - { + LWasmTruncateToInt64(const LAllocation& in) { setOperand(0, in); } diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 83437c79823e4..e2e5bbe6c5ced 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -351,13 +351,13 @@ LIRGeneratorARM::lowerDivI(MDiv* div) return; } - gen->setPerformsCall(); + LSoftDivI* lir = new(alloc()) LSoftDivI(useFixedAtStart(div->lhs(), r0), + useFixedAtStart(div->rhs(), r1)); - LSoftDivI* lir = new(alloc()) LSoftDivI(useFixedAtStart(div->lhs(), r0), useFixedAtStart(div->rhs(), r1), - tempFixed(r1), tempFixed(r2), tempFixed(r3)); if (div->fallible()) assignSnapshot(lir, Bailout_DoubleOutput); - defineFixed(lir, div, LAllocation(AnyRegister(r0))); + + defineReturn(lir, div); } void @@ -405,14 +405,14 @@ LIRGeneratorARM::lowerModI(MMod* mod) return; } - gen->setPerformsCall(); + LSoftModI* lir = new(alloc()) LSoftModI(useFixedAtStart(mod->lhs(), r0), + useFixedAtStart(mod->rhs(), r1), + temp()); - LSoftModI* lir = new(alloc()) LSoftModI(useFixedAtStart(mod->lhs(), r0), useFixedAtStart(mod->rhs(), r1), - tempFixed(r0), tempFixed(r2), tempFixed(r3), - temp(LDefinition::GENERAL)); if (mod->fallible()) assignSnapshot(lir, Bailout_DoubleOutput); - defineFixed(lir, mod, LAllocation(AnyRegister(r1))); + + defineReturn(lir, mod); } void @@ -423,8 +423,6 @@ LIRGeneratorARM::lowerDivI64(MDiv* div) return; } - gen->setPerformsCall(); - LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); defineReturn(lir, div); @@ -438,8 +436,6 @@ LIRGeneratorARM::lowerModI64(MMod* mod) return; } - gen->setPerformsCall(); - LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64RegisterAtStart(mod->lhs()), useInt64RegisterAtStart(mod->rhs())); defineReturn(lir, mod); @@ -448,8 +444,6 @@ LIRGeneratorARM::lowerModI64(MMod* mod) void LIRGeneratorARM::lowerUDivI64(MDiv* div) { - gen->setPerformsCall(); - LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); defineReturn(lir, div); @@ -458,8 +452,6 @@ LIRGeneratorARM::lowerUDivI64(MDiv* div) void LIRGeneratorARM::lowerUModI64(MMod* mod) { - gen->setPerformsCall(); - LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64RegisterAtStart(mod->lhs()), useInt64RegisterAtStart(mod->rhs())); defineReturn(lir, mod); @@ -573,13 +565,13 @@ LIRGeneratorARM::lowerUDiv(MDiv* div) return; } - gen->setPerformsCall(); + LSoftUDivOrMod* lir = new(alloc()) LSoftUDivOrMod(useFixedAtStart(lhs, r0), + useFixedAtStart(rhs, r1)); - LSoftUDivOrMod* lir = new(alloc()) LSoftUDivOrMod(useFixedAtStart(lhs, r0), useFixedAtStart(rhs, r1), - tempFixed(r1), tempFixed(r2), tempFixed(r3)); if (div->fallible()) assignSnapshot(lir, Bailout_DoubleOutput); - defineFixed(lir, div, LAllocation(AnyRegister(r0))); + + defineReturn(lir, div); } void @@ -598,13 +590,13 @@ LIRGeneratorARM::lowerUMod(MMod* mod) return; } - gen->setPerformsCall(); + LSoftUDivOrMod* lir = new(alloc()) LSoftUDivOrMod(useFixedAtStart(lhs, r0), + useFixedAtStart(rhs, r1)); - LSoftUDivOrMod* lir = new(alloc()) LSoftUDivOrMod(useFixedAtStart(lhs, r0), useFixedAtStart(rhs, r1), - tempFixed(r0), tempFixed(r2), tempFixed(r3)); if (mod->fallible()) assignSnapshot(lir, Bailout_DoubleOutput); - defineFixed(lir, mod, LAllocation(AnyRegister(r1))); + + defineReturn(lir, mod); } void @@ -910,8 +902,6 @@ LIRGeneratorARM::visitAsmJSCompareExchangeHeap(MAsmJSCompareExchangeHeap* ins) MOZ_ASSERT(base->type() == MIRType::Int32); if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) { - gen->setPerformsCall(); - LAsmJSCompareExchangeCallout* lir = new(alloc()) LAsmJSCompareExchangeCallout(useFixedAtStart(base, IntArgReg2), useFixedAtStart(ins->oldValue(), IntArgReg3), @@ -939,8 +929,6 @@ LIRGeneratorARM::visitAsmJSAtomicExchangeHeap(MAsmJSAtomicExchangeHeap* ins) MOZ_ASSERT(ins->access().offset() == 0); if (byteSize(ins->access().type()) < 4 && !HasLDSTREXBHD()) { - gen->setPerformsCall(); - // Call out on ARMv6. defineReturn(new(alloc()) LAsmJSAtomicExchangeCallout(useFixedAtStart(ins->base(), IntArgReg2), useFixedAtStart(ins->value(), IntArgReg3), @@ -965,8 +953,6 @@ LIRGeneratorARM::visitAsmJSAtomicBinopHeap(MAsmJSAtomicBinopHeap* ins) MOZ_ASSERT(base->type() == MIRType::Int32); if (byteSize(ins->access().type()) != 4 && !HasLDSTREXBHD()) { - gen->setPerformsCall(); - LAsmJSAtomicBinopCallout* lir = new(alloc()) LAsmJSAtomicBinopCallout(useFixedAtStart(base, IntArgReg2), useFixedAtStart(ins->value(), IntArgReg3), @@ -1024,8 +1010,6 @@ LIRGeneratorARM::visitWasmTruncateToInt64(MWasmTruncateToInt64* ins) MDefinition* opd = ins->input(); MOZ_ASSERT(opd->type() == MIRType::Double || opd->type() == MIRType::Float32); - gen->setPerformsCall(); - defineReturn(new(alloc()) LWasmTruncateToInt64(useRegisterAtStart(opd)), ins); } @@ -1034,9 +1018,7 @@ LIRGeneratorARM::visitInt64ToFloatingPoint(MInt64ToFloatingPoint* ins) { MOZ_ASSERT(ins->type() == MIRType::Double || ins->type() == MIRType::Float32); - gen->setPerformsCall(); - - auto lir = new(alloc()) LInt64ToFloatingPointCall(); + auto* lir = new(alloc()) LInt64ToFloatingPointCall(); lir->setInt64Operand(0, useInt64RegisterAtStart(ins->input())); defineReturn(lir, ins); } diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 1b13a7f68417c..49e7bb856eaba 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -65,7 +65,8 @@ LIRGeneratorShared::define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, } template void -LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y>* lir, MDefinition* mir, const LAllocation& output) +LIRGeneratorShared::defineFixed(LInstructionHelper<1, X, Y>* lir, MDefinition* mir, + const LAllocation& output) { LDefinition::Type type = LDefinition::TypeFrom(mir->type()); @@ -234,6 +235,7 @@ LIRGeneratorShared::defineReturn(LInstruction* lir, MDefinition* mir) lir->setMir(mir); MOZ_ASSERT(lir->isCall()); + gen->setPerformsCall(); uint32_t vreg = getVirtualRegister(); diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 19987bfba65ab..337b165a56ed2 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -618,8 +618,6 @@ LIRGeneratorX86::lowerDivI64(MDiv* div) return; } - gen->setPerformsCall(); - LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); @@ -634,8 +632,6 @@ LIRGeneratorX86::lowerModI64(MMod* mod) return; } - gen->setPerformsCall(); - LDivOrModI64* lir = new(alloc()) LDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); @@ -645,8 +641,6 @@ LIRGeneratorX86::lowerModI64(MMod* mod) void LIRGeneratorX86::lowerUDivI64(MDiv* div) { - gen->setPerformsCall(); - LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); @@ -656,8 +650,6 @@ LIRGeneratorX86::lowerUDivI64(MDiv* div) void LIRGeneratorX86::lowerUModI64(MMod* mod) { - gen->setPerformsCall(); - LUDivOrModI64* lir = new(alloc()) LUDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); From 017583b3a474553a540f10d30360b8d2a16ef39e Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Thu, 27 Apr 2017 15:44:45 +0200 Subject: [PATCH 083/131] Bug 1352506: Don't conflate need for overrecursed check and static alignment in MIRGenerator; r=jandem --- js/src/jit/Lowering.cpp | 39 +++++++++++----------- js/src/jit/MIRGenerator.h | 20 ++++++++--- js/src/jit/MIRGraph.cpp | 3 +- js/src/jit/shared/CodeGenerator-shared.cpp | 8 ++--- js/src/jit/shared/Lowering-shared-inl.h | 2 +- 5 files changed, 41 insertions(+), 31 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 3922582a2c1c0..e19d33bb9e353 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2265,13 +2265,13 @@ LIRGenerator::visitTruncateToInt32(MTruncateToInt32* truncate) case MIRType::Double: // May call into JS::ToInt32() on the slow OOL path. - gen->setPerformsCall(); + gen->setNeedsStaticStackAlignment(); lowerTruncateDToInt32(truncate); break; case MIRType::Float32: // May call into JS::ToInt32() on the slow OOL path. - gen->setPerformsCall(); + gen->setNeedsStaticStackAlignment(); lowerTruncateFToInt32(truncate); break; @@ -3705,10 +3705,9 @@ LIRGenerator::visitGetNameCache(MGetNameCache* ins) { MOZ_ASSERT(ins->envObj()->type() == MIRType::Object); - // Set the performs-call flag so that we don't omit the overrecursed check. - // This is necessary because the cache can attach a scripted getter stub - // that calls this script recursively. - gen->setPerformsCall(); + // Emit an overrecursed check: this is necessary because the cache can + // attach a scripted getter stub that calls this script recursively. + gen->setNeedsOverrecursedCheck(); LGetNameCache* lir = new(alloc()) LGetNameCache(useRegister(ins->envObj()), temp()); defineBox(lir, ins); @@ -3736,10 +3735,9 @@ LIRGenerator::visitGetPropertyCache(MGetPropertyCache* ins) id->type() == MIRType::Value); if (ins->monitoredResult()) { - // Set the performs-call flag so that we don't omit the overrecursed - // check. This is necessary because the cache can attach a scripted - // getter stub that calls this script recursively. - gen->setPerformsCall(); + // Emit an overrecursed check: this is necessary because the cache can + // attach a scripted getter stub that calls this script recursively. + gen->setNeedsOverrecursedCheck(); } // If this is a GETPROP, the id is a constant string. Allow passing it as a @@ -4011,10 +4009,9 @@ LIRGenerator::visitSetPropertyCache(MSetPropertyCache* ins) bool useConstId = id->type() == MIRType::String || id->type() == MIRType::Symbol; bool useConstValue = IsNonNurseryConstant(ins->value()); - // Set the performs-call flag so that we don't omit the overrecursed check. - // This is necessary because the cache can attach a scripted setter stub - // that calls this script recursively. - gen->setPerformsCall(); + // Emit an overrecursed check: this is necessary because the cache can + // attach a scripted setter stub that calls this script recursively. + gen->setNeedsOverrecursedCheck(); // We need a double/float32 temp register for typed array stubs if this is // a SETELEM or INITELEM op. @@ -4212,11 +4209,11 @@ LIRGenerator::visitHasOwnCache(MHasOwnCache* ins) id->type() == MIRType::Int32 || id->type() == MIRType::Value); - gen->setPerformsCall(); + // Emit an overrecursed check: this is necessary because the cache can + // attach a scripted getter stub that calls this script recursively. + gen->setNeedsOverrecursedCheck(); - LHasOwnCache* lir = - new(alloc()) LHasOwnCache(useBoxOrTyped(value), - useBoxOrTyped(id)); + LHasOwnCache* lir = new(alloc()) LHasOwnCache(useBoxOrTyped(value), useBoxOrTyped(id)); define(lir, ins); assignSafepoint(lir, ins); } @@ -4933,8 +4930,10 @@ LIRGenerator::visitInstruction(MInstruction* ins) return false; ins->accept(this); - if (ins->possiblyCalls()) - gen->setPerformsCall(); + if (ins->possiblyCalls()) { + gen->setNeedsStaticStackAlignment(); + gen->setNeedsOverrecursedCheck(); + } if (ins->resumePoint()) updateResumeState(ins); diff --git a/js/src/jit/MIRGenerator.h b/js/src/jit/MIRGenerator.h index 5e686433b87e0..8ccc567354a84 100644 --- a/js/src/jit/MIRGenerator.h +++ b/js/src/jit/MIRGenerator.h @@ -146,12 +146,21 @@ class MIRGenerator uint32_t minWasmHeapLength() const { return minWasmHeapLength_; } - void setPerformsCall() { - performsCall_ = true; + + void setNeedsOverrecursedCheck() { + needsOverrecursedCheck_ = true; + } + bool needsOverrecursedCheck() const { + return needsOverrecursedCheck_; } - bool performsCall() const { - return performsCall_; + + void setNeedsStaticStackAlignment() { + needsStaticStackAlignment_ = true; } + bool needsStaticStackAlignment() const { + return needsOverrecursedCheck_; + } + // Traverses the graph to find if there's any SIMD instruction. Costful but // the value is cached, so don't worry about calling it several times. bool usesSimd(); @@ -182,7 +191,8 @@ class MIRGenerator mozilla::Atomic cancelBuild_; uint32_t wasmMaxStackArgBytes_; - bool performsCall_; + bool needsOverrecursedCheck_; + bool needsStaticStackAlignment_; bool usesSimd_; bool cachedUsesSimd_; diff --git a/js/src/jit/MIRGraph.cpp b/js/src/jit/MIRGraph.cpp index 769c073d9c30a..200d7ce679994 100644 --- a/js/src/jit/MIRGraph.cpp +++ b/js/src/jit/MIRGraph.cpp @@ -30,7 +30,8 @@ MIRGenerator::MIRGenerator(CompileCompartment* compartment, const JitCompileOpti pauseBuild_(nullptr), cancelBuild_(false), wasmMaxStackArgBytes_(0), - performsCall_(false), + needsOverrecursedCheck_(false), + needsStaticStackAlignment_(false), usesSimd_(false), cachedUsesSimd_(false), modifiesFrameArguments_(false), diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index e5442d0f50990..5008735909168 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -89,14 +89,14 @@ CodeGeneratorShared::CodeGeneratorShared(MIRGenerator* gen, LIRGraph* graph, Mac if (gen->usesSimd()) { // If the function uses any SIMD then we may need to insert padding // so that local slots are aligned for SIMD. - frameInitialAdjustment_ = ComputeByteAlignment(sizeof(wasm::Frame), - WasmStackAlignment); + frameInitialAdjustment_ = ComputeByteAlignment(sizeof(wasm::Frame), WasmStackAlignment); frameDepth_ += frameInitialAdjustment_; + // Keep the stack aligned. Some SIMD sequences build values on the // stack and need the stack aligned. frameDepth_ += ComputeByteAlignment(sizeof(wasm::Frame) + frameDepth_, WasmStackAlignment); - } else if (gen->performsCall()) { + } else if (gen->needsStaticStackAlignment()) { // An MWasmCall does not align the stack pointer at calls sites but // instead relies on the a priori stack adjustment. This must be the // last adjustment of frameDepth_. @@ -1490,7 +1490,7 @@ CodeGeneratorShared::omitOverRecursedCheck() const // stack overflow check. Note that the actual number here is somewhat // arbitrary, and codegen actually uses small bounded amounts of // additional stack space in some cases too. - return frameSize() < 64 && !gen->performsCall(); + return frameSize() < 64 && !gen->needsOverrecursedCheck(); } void diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index 49e7bb856eaba..a89c958878164 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -235,7 +235,7 @@ LIRGeneratorShared::defineReturn(LInstruction* lir, MDefinition* mir) lir->setMir(mir); MOZ_ASSERT(lir->isCall()); - gen->setPerformsCall(); + gen->setNeedsStaticStackAlignment(); uint32_t vreg = getVirtualRegister(); From 6f006141b43f25ab5a5f65450cc5e9f9b9e67dd2 Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 26 Apr 2017 17:46:14 +0200 Subject: [PATCH 084/131] Bug 1360211: Remove dead CalleeStackOffset; r=jandem --- js/src/jit/shared/CodeGenerator-shared-inl.h | 6 ------ js/src/jit/shared/CodeGenerator-shared.h | 3 --- 2 files changed, 9 deletions(-) diff --git a/js/src/jit/shared/CodeGenerator-shared-inl.h b/js/src/jit/shared/CodeGenerator-shared-inl.h index 662e2fa5d6ae1..c40aab4bcd7eb 100644 --- a/js/src/jit/shared/CodeGenerator-shared-inl.h +++ b/js/src/jit/shared/CodeGenerator-shared-inl.h @@ -226,12 +226,6 @@ CodeGeneratorShared::ArgToStackOffset(int32_t slot) const slot; } -int32_t -CodeGeneratorShared::CalleeStackOffset() const -{ - return masm.framePushed() + JitFrameLayout::offsetOfCalleeToken(); -} - int32_t CodeGeneratorShared::SlotToStackOffset(int32_t slot) const { diff --git a/js/src/jit/shared/CodeGenerator-shared.h b/js/src/jit/shared/CodeGenerator-shared.h index af54bc5f785b1..d75572dac0a67 100644 --- a/js/src/jit/shared/CodeGenerator-shared.h +++ b/js/src/jit/shared/CodeGenerator-shared.h @@ -213,9 +213,6 @@ class CodeGeneratorShared : public LElementVisitor // For arguments to the current function. inline int32_t ArgToStackOffset(int32_t slot) const; - // For the callee of the current function. - inline int32_t CalleeStackOffset() const; - inline int32_t SlotToStackOffset(int32_t slot) const; inline int32_t StackOffsetToSlot(int32_t offset) const; From 6a9ea4cf758ea1b5853c58751b095486408d202d Mon Sep 17 00:00:00 2001 From: Benjamin Bouvier Date: Wed, 26 Apr 2017 18:31:26 +0200 Subject: [PATCH 085/131] Bug 1360211: Remove EntryFrameLayout; r=jandem --- js/src/gdb/mozilla/unwind.py | 2 -- js/src/jit/JitFrames.h | 18 +++++------------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/js/src/gdb/mozilla/unwind.py b/js/src/gdb/mozilla/unwind.py index 4e7ab91f06798..6020c2d6b40a1 100644 --- a/js/src/gdb/mozilla/unwind.py +++ b/js/src/gdb/mozilla/unwind.py @@ -40,8 +40,6 @@ def debug(something): 'JitFrame_BaselineJS': 'JitFrameLayout', 'JitFrame_BaselineStub': 'BaselineStubFrameLayout', 'JitFrame_IonStub': 'JitStubFrameLayout', - # Technically EntryFrameLayout, but that doesn't wind up in the - # debuginfo because there are no uses of it. 'JitFrame_Entry': 'JitFrameLayout', 'JitFrame_Rectifier': 'RectifierFrameLayout', 'JitFrame_IonAccessorIC': 'IonAccessorICFrameLayout', diff --git a/js/src/jit/JitFrames.h b/js/src/jit/JitFrames.h index 2b8df56aabb9c..e165a7d52b963 100644 --- a/js/src/jit/JitFrames.h +++ b/js/src/jit/JitFrames.h @@ -95,10 +95,11 @@ ScriptFromCalleeToken(CalleeToken token) // to. The exact mechanism in which frames are laid out is architecture // dependent. // -// Two special frame types exist. Entry frames begin an ion activation, and -// therefore there is exactly one per activation of jit::Cannon. Exit frames -// are necessary to leave JIT code and enter C++, and thus, C++ code will -// always begin iterating from the topmost exit frame. +// Two special frame types exist: +// - Entry frames begin a JitActivation, and therefore there is exactly one +// per activation of EnterIon or EnterBaseline. These reuse JitFrameLayout. +// - Exit frames are necessary to leave JIT code and enter C++, and thus, +// C++ code will always begin iterating from the topmost exit frame. class LSafepoint; @@ -438,15 +439,6 @@ class JitFrameLayout : public CommonFrameLayout } }; -// this is the layout of the frame that is used when we enter Ion code from platform ABI code -class EntryFrameLayout : public JitFrameLayout -{ - public: - static inline size_t Size() { - return sizeof(EntryFrameLayout); - } -}; - class RectifierFrameLayout : public JitFrameLayout { public: From e38f7cd7d4d14f302bb8083073c61739190c4e74 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Thu, 4 May 2017 12:57:14 -0400 Subject: [PATCH 086/131] Bug 1362108 - Update pdf.js to version 1.8.314. r=bdahl --- browser/extensions/pdfjs/README.mozilla | 4 +- browser/extensions/pdfjs/content/build/pdf.js | 297 +++++++++-------- .../pdfjs/content/build/pdf.worker.js | 311 +++++++++++------- 3 files changed, 336 insertions(+), 276 deletions(-) diff --git a/browser/extensions/pdfjs/README.mozilla b/browser/extensions/pdfjs/README.mozilla index 8e5cb97c654d4..79c6f345d0efc 100644 --- a/browser/extensions/pdfjs/README.mozilla +++ b/browser/extensions/pdfjs/README.mozilla @@ -1,5 +1,5 @@ This is the PDF.js project output, https://github.com/mozilla/pdf.js -Current extension version is: 1.8.290 +Current extension version is: 1.8.314 -Taken from upstream commit: 60c232bc +Taken from upstream commit: 3adda80f diff --git a/browser/extensions/pdfjs/content/build/pdf.js b/browser/extensions/pdfjs/content/build/pdf.js index cc838618793ef..f107391446525 100644 --- a/browser/extensions/pdfjs/content/build/pdf.js +++ b/browser/extensions/pdfjs/content/build/pdf.js @@ -992,7 +992,7 @@ function MessageHandler(sourceName, targetName, comObj) { this.postMessageTransfers = true; var callbacksCapabilities = this.callbacksCapabilities = Object.create(null); var ah = this.actionHandler = Object.create(null); - this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { + this._onComObjOnMessage = event => { var data = event.data; if (data.targetName !== this.sourceName) { return; @@ -1043,7 +1043,7 @@ function MessageHandler(sourceName, targetName, comObj) { } else { error('Unknown action from worker: ' + data.action); } - }.bind(this); + }; comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { @@ -1225,14 +1225,14 @@ var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() { if (!name) { return Promise.reject(new Error('CMap name must be specified.')); } - return new Promise(function (resolve, reject) { + return new Promise((resolve, reject) => { var url = this.baseUrl + name + (this.isCompressed ? '.bcmap' : ''); var request = new XMLHttpRequest(); request.open('GET', url, true); if (this.isCompressed) { request.responseType = 'arraybuffer'; } - request.onreadystatechange = function () { + request.onreadystatechange = () => { if (request.readyState !== XMLHttpRequest.DONE) { return; } @@ -1252,9 +1252,9 @@ var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() { } } reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url)); - }.bind(this); + }; request.send(null); - }.bind(this)); + }); } }; return DOMCMapReaderFactory; @@ -1598,12 +1598,11 @@ var LinkAnnotationElement = function LinkAnnotationElementClosure() { this.container.appendChild(link); return this.container; }, - _bindLink: function LinkAnnotationElement_bindLink(link, destination) { - var self = this; + _bindLink(link, destination) { link.href = this.linkService.getDestinationHash(destination); - link.onclick = function () { + link.onclick = () => { if (destination) { - self.linkService.navigateTo(destination); + this.linkService.navigateTo(destination); } return false; }; @@ -1611,11 +1610,10 @@ var LinkAnnotationElement = function LinkAnnotationElementClosure() { link.className = 'internalLink'; } }, - _bindNamedAction: function LinkAnnotationElement_bindNamedAction(link, action) { - var self = this; + _bindNamedAction(link, action) { link.href = this.linkService.getAnchorUrl(''); - link.onclick = function () { - self.linkService.executeNamedAction(action); + link.onclick = () => { + this.linkService.executeNamedAction(action); return false; }; link.className = 'internalLink'; @@ -2082,7 +2080,7 @@ exports.AnnotationLayer = AnnotationLayer; Object.defineProperty(exports, "__esModule", { value: true }); -exports.build = exports.version = exports._UnsupportedManager = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.getDocument = undefined; +exports.build = exports.version = exports._UnsupportedManager = exports.PDFPageProxy = exports.PDFDocumentProxy = exports.PDFWorker = exports.PDFDataRangeTransport = exports.LoopbackPort = exports.getDocument = undefined; var _util = __w_pdfjs_require__(0); @@ -2239,13 +2237,13 @@ var PDFDocumentLoadingTask = function PDFDocumentLoadingTaskClosure() { destroy() { this.destroyed = true; var transportDestroyed = !this._transport ? Promise.resolve() : this._transport.destroy(); - return transportDestroyed.then(function () { + return transportDestroyed.then(() => { this._transport = null; if (this._worker) { this._worker.destroy(); this._worker = null; } - }.bind(this)); + }); }, then: function PDFDocumentLoadingTask_then(onFulfilled, onRejected) { return this.promise.then.apply(this.promise, arguments); @@ -2279,20 +2277,20 @@ var PDFDataRangeTransport = function pdfDataRangeTransportClosure() { } }, onDataProgress: function PDFDataRangeTransport_onDataProgress(loaded) { - this._readyCapability.promise.then(function () { + this._readyCapability.promise.then(() => { var listeners = this._progressListeners; for (var i = 0, n = listeners.length; i < n; ++i) { listeners[i](loaded); } - }.bind(this)); + }); }, onDataProgressiveRead: function PDFDataRangeTransport_onDataProgress(chunk) { - this._readyCapability.promise.then(function () { + this._readyCapability.promise.then(() => { var listeners = this._progressiveReadListeners; for (var i = 0, n = listeners.length; i < n; ++i) { listeners[i](chunk); } - }.bind(this)); + }); }, transportReady: function PDFDataRangeTransport_transportReady() { this._readyCapability.resolve(); @@ -2431,6 +2429,23 @@ var PDFPageProxy = function PDFPageProxyClosure() { renderInteractiveForms: params.renderInteractiveForms === true }); } + var complete = error => { + var i = intentState.renderTasks.indexOf(internalRenderTask); + if (i >= 0) { + intentState.renderTasks.splice(i, 1); + } + if (this.cleanupAfterRender) { + this.pendingCleanup = true; + } + this._tryCleanup(); + if (error) { + internalRenderTask.capability.reject(error); + } else { + internalRenderTask.capability.resolve(); + } + stats.timeEnd('Rendering'); + stats.timeEnd('Overall'); + }; var internalRenderTask = new InternalRenderTask(complete, params, this.objs, this.commonObjs, intentState.operatorList, this.pageNumber, canvasFactory); internalRenderTask.useRequestAnimationFrame = renderingIntent !== 'print'; if (!intentState.renderTasks) { @@ -2442,35 +2457,15 @@ var PDFPageProxy = function PDFPageProxyClosure() { (0, _util.deprecated)('render is used with continueCallback parameter'); renderTask.onContinue = params.continueCallback; } - var self = this; - intentState.displayReadyCapability.promise.then(function pageDisplayReadyPromise(transparency) { - if (self.pendingCleanup) { + intentState.displayReadyCapability.promise.then(transparency => { + if (this.pendingCleanup) { complete(); return; } stats.time('Rendering'); internalRenderTask.initializeGraphics(transparency); internalRenderTask.operatorListChanged(); - }, function pageDisplayReadPromiseError(reason) { - complete(reason); - }); - function complete(error) { - var i = intentState.renderTasks.indexOf(internalRenderTask); - if (i >= 0) { - intentState.renderTasks.splice(i, 1); - } - if (self.cleanupAfterRender) { - self.pendingCleanup = true; - } - self._tryCleanup(); - if (error) { - internalRenderTask.capability.reject(error); - } else { - internalRenderTask.capability.resolve(); - } - stats.timeEnd('Rendering'); - stats.timeEnd('Overall'); - } + }, complete); return renderTask; }, getOperatorList: function PDFPageProxy_getOperatorList() { @@ -2583,6 +2578,74 @@ var PDFPageProxy = function PDFPageProxyClosure() { }; return PDFPageProxy; }(); +class LoopbackPort { + constructor(defer) { + this._listeners = []; + this._defer = defer; + this._deferred = Promise.resolve(undefined); + } + postMessage(obj, transfers) { + function cloneValue(value) { + if (typeof value !== 'object' || value === null) { + return value; + } + if (cloned.has(value)) { + return cloned.get(value); + } + var result; + var buffer; + if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) { + var transferable = transfers && transfers.indexOf(buffer) >= 0; + if (value === buffer) { + result = value; + } else if (transferable) { + result = new value.constructor(buffer, value.byteOffset, value.byteLength); + } else { + result = new value.constructor(value); + } + cloned.set(value, result); + return result; + } + result = (0, _util.isArray)(value) ? [] : {}; + cloned.set(value, result); + for (var i in value) { + var desc, + p = value; + while (!(desc = Object.getOwnPropertyDescriptor(p, i))) { + p = Object.getPrototypeOf(p); + } + if (typeof desc.value === 'undefined' || typeof desc.value === 'function') { + continue; + } + result[i] = cloneValue(desc.value); + } + return result; + } + if (!this._defer) { + this._listeners.forEach(function (listener) { + listener.call(this, { data: obj }); + }, this); + return; + } + var cloned = new WeakMap(); + var e = { data: cloneValue(obj) }; + this._deferred.then(() => { + this._listeners.forEach(function (listener) { + listener.call(this, e); + }, this); + }); + } + addEventListener(name, listener) { + this._listeners.push(listener); + } + removeEventListener(name, listener) { + var i = this._listeners.indexOf(listener); + this._listeners.splice(i, 1); + } + terminate() { + this._listeners = []; + } +} var PDFWorker = function PDFWorkerClosure() { var nextFakeWorkerId = 0; function getWorkerSrc() { @@ -2609,74 +2672,6 @@ var PDFWorker = function PDFWorkerClosure() { loader(fakeWorkerFilesLoadedCapability.resolve); return fakeWorkerFilesLoadedCapability.promise; } - function FakeWorkerPort(defer) { - this._listeners = []; - this._defer = defer; - this._deferred = Promise.resolve(undefined); - } - FakeWorkerPort.prototype = { - postMessage(obj, transfers) { - function cloneValue(value) { - if (typeof value !== 'object' || value === null) { - return value; - } - if (cloned.has(value)) { - return cloned.get(value); - } - var result; - var buffer; - if ((buffer = value.buffer) && (0, _util.isArrayBuffer)(buffer)) { - var transferable = transfers && transfers.indexOf(buffer) >= 0; - if (value === buffer) { - result = value; - } else if (transferable) { - result = new value.constructor(buffer, value.byteOffset, value.byteLength); - } else { - result = new value.constructor(value); - } - cloned.set(value, result); - return result; - } - result = (0, _util.isArray)(value) ? [] : {}; - cloned.set(value, result); - for (var i in value) { - var desc, - p = value; - while (!(desc = Object.getOwnPropertyDescriptor(p, i))) { - p = Object.getPrototypeOf(p); - } - if (typeof desc.value === 'undefined' || typeof desc.value === 'function') { - continue; - } - result[i] = cloneValue(desc.value); - } - return result; - } - if (!this._defer) { - this._listeners.forEach(function (listener) { - listener.call(this, { data: obj }); - }, this); - return; - } - var cloned = new WeakMap(); - var e = { data: cloneValue(obj) }; - this._deferred.then(function () { - this._listeners.forEach(function (listener) { - listener.call(this, e); - }, this); - }.bind(this)); - }, - addEventListener(name, listener) { - this._listeners.push(listener); - }, - removeEventListener(name, listener) { - var i = this._listeners.indexOf(listener); - this._listeners.splice(i, 1); - }, - terminate() { - this._listeners = []; - } - }; function createCDNWrapper(url) { var wrapper = 'importScripts(\'' + url + '\');'; return URL.createObjectURL(new Blob([wrapper])); @@ -2716,7 +2711,7 @@ var PDFWorker = function PDFWorkerClosure() { try { var worker = new Worker(workerSrc); var messageHandler = new _util.MessageHandler('main', 'worker', worker); - var terminateEarly = function () { + var terminateEarly = () => { worker.removeEventListener('error', onWorkerError); messageHandler.destroy(); worker.terminate(); @@ -2725,14 +2720,14 @@ var PDFWorker = function PDFWorkerClosure() { } else { this._setupFakeWorker(); } - }.bind(this); - var onWorkerError = function (event) { + }; + var onWorkerError = () => { if (!this._webWorker) { terminateEarly(); } - }.bind(this); + }; worker.addEventListener('error', onWorkerError); - messageHandler.on('test', function PDFWorker_test(data) { + messageHandler.on('test', data => { worker.removeEventListener('error', onWorkerError); if (this.destroyed) { terminateEarly(); @@ -2753,14 +2748,14 @@ var PDFWorker = function PDFWorkerClosure() { messageHandler.destroy(); worker.terminate(); } - }.bind(this)); + }); messageHandler.on('console_log', function (data) { console.log.apply(console, data); }); messageHandler.on('console_error', function (data) { console.error.apply(console, data); }); - messageHandler.on('ready', function (data) { + messageHandler.on('ready', data => { worker.removeEventListener('error', onWorkerError); if (this.destroyed) { terminateEarly(); @@ -2771,7 +2766,7 @@ var PDFWorker = function PDFWorkerClosure() { } catch (e) { this._setupFakeWorker(); } - }.bind(this)); + }); var sendTest = function () { var postMessageTransfers = (0, _dom_utils.getDefaultSetting)('postMessageTransfers') && !isPostMessageTransfersDisabled; var testObj = new Uint8Array([postMessageTransfers ? 255 : 0]); @@ -2796,13 +2791,13 @@ var PDFWorker = function PDFWorkerClosure() { (0, _util.warn)('Setting up fake worker.'); isWorkerDisabled = true; } - setupFakeWorkerGlobal().then(function (WorkerMessageHandler) { + setupFakeWorkerGlobal().then(WorkerMessageHandler => { if (this.destroyed) { this._readyCapability.reject(new Error('Worker was destroyed')); return; } var isTypedArraysPresent = Uint8Array !== Float32Array; - var port = new FakeWorkerPort(isTypedArraysPresent); + var port = new LoopbackPort(isTypedArraysPresent); this._port = port; var id = 'fake' + nextFakeWorkerId++; var workerHandler = new _util.MessageHandler(id + '_worker', id, port); @@ -2810,7 +2805,7 @@ var PDFWorker = function PDFWorkerClosure() { var messageHandler = new _util.MessageHandler(id, id + '_worker', port); this._messageHandler = messageHandler; this._readyCapability.resolve(); - }.bind(this)); + }); }, destroy: function PDFWorker_destroy() { this.destroyed = true; @@ -2864,20 +2859,19 @@ var WorkerTransport = function WorkerTransportClosure() { }); this.pageCache = []; this.pagePromises = []; - var self = this; var terminated = this.messageHandler.sendWithPromise('Terminate', null); waitOn.push(terminated); - Promise.all(waitOn).then(function () { - self.fontLoader.clear(); - if (self.pdfDataRangeTransport) { - self.pdfDataRangeTransport.abort(); - self.pdfDataRangeTransport = null; + Promise.all(waitOn).then(() => { + this.fontLoader.clear(); + if (this.pdfDataRangeTransport) { + this.pdfDataRangeTransport.abort(); + this.pdfDataRangeTransport = null; } - if (self.messageHandler) { - self.messageHandler.destroy(); - self.messageHandler = null; + if (this.messageHandler) { + this.messageHandler.destroy(); + this.messageHandler = null; } - self.destroyCapability.resolve(); + this.destroyCapability.resolve(); }, this.destroyCapability.reject); return this.destroyCapability.promise; }, @@ -2991,9 +2985,10 @@ var WorkerTransport = function WorkerTransportClosure() { disableFontFace: (0, _dom_utils.getDefaultSetting)('disableFontFace'), fontRegistry }); - this.fontLoader.bind([font], function fontReady(fontObjs) { + var fontReady = fontObjs => { this.commonObjs.resolve(id, font); - }.bind(this)); + }; + this.fontLoader.bind([font], fontReady); break; case 'FontPath': this.commonObjs.resolve(id, data[2]); @@ -3140,14 +3135,14 @@ var WorkerTransport = function WorkerTransportClosure() { if (pageIndex in this.pagePromises) { return this.pagePromises[pageIndex]; } - var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex }).then(function (pageInfo) { + var promise = this.messageHandler.sendWithPromise('GetPage', { pageIndex }).then(pageInfo => { if (this.destroyed) { throw new Error('Transport destroyed'); } var page = new PDFPageProxy(pageIndex, pageInfo, this); this.pageCache[pageIndex] = page; return page; - }.bind(this)); + }); this.pagePromises[pageIndex] = promise; return promise; }, @@ -3192,7 +3187,7 @@ var WorkerTransport = function WorkerTransportClosure() { return this.messageHandler.sendWithPromise('GetStats', null); }, startCleanup: function WorkerTransport_startCleanup() { - this.messageHandler.sendWithPromise('Cleanup', null).then(function endCleanup() { + this.messageHandler.sendWithPromise('Cleanup', null).then(() => { for (var i = 0, ii = this.pageCache.length; i < ii; i++) { var page = this.pageCache[i]; if (page) { @@ -3201,7 +3196,7 @@ var WorkerTransport = function WorkerTransportClosure() { } this.commonObjs.clear(); this.fontLoader.clear(); - }.bind(this)); + }); } }; return WorkerTransport; @@ -3391,10 +3386,11 @@ var _UnsupportedManager = function UnsupportedManagerClosure() { }(); var version, build; { - exports.version = version = '1.8.290'; - exports.build = build = '60c232bc'; + exports.version = version = '1.8.314'; + exports.build = build = '3adda80f'; } exports.getDocument = getDocument; +exports.LoopbackPort = LoopbackPort; exports.PDFDataRangeTransport = PDFDataRangeTransport; exports.PDFWorker = PDFWorker; exports.PDFDocumentProxy = PDFDocumentProxy; @@ -3845,10 +3841,9 @@ var renderTextLayer = function renderTextLayerClosure() { if (!timeout) { render(this); } else { - var self = this; - this._renderTimer = setTimeout(function () { - render(self); - self._renderTimer = null; + this._renderTimer = setTimeout(() => { + render(this); + this._renderTimer = null; }, timeout); } }, @@ -4394,8 +4389,8 @@ if (!_util.globalScope.PDFJS) { } var PDFJS = _util.globalScope.PDFJS; { - PDFJS.version = '1.8.290'; - PDFJS.build = '60c232bc'; + PDFJS.version = '1.8.314'; + PDFJS.build = '3adda80f'; } PDFJS.pdfBug = false; if (PDFJS.verbosity !== undefined) { @@ -4458,6 +4453,7 @@ PDFJS.isEvalSupported = PDFJS.isEvalSupported === undefined ? true : PDFJS.isEva PDFJS.pdfjsNext = PDFJS.pdfjsNext === undefined ? false : PDFJS.pdfjsNext; ; PDFJS.getDocument = _api.getDocument; +PDFJS.LoopbackPort = _api.LoopbackPort; PDFJS.PDFDataRangeTransport = _api.PDFDataRangeTransport; PDFJS.PDFWorker = _api.PDFWorker; PDFJS.hasCanvasTypedArrays = true; @@ -5733,7 +5729,7 @@ var CanvasGraphics = function CanvasGraphicsClosure() { var spacing = (glyph.isSpace ? wordSpacing : 0) + charSpacing; var operatorList = font.charProcOperatorList[glyph.operatorListId]; if (!operatorList) { - (0, _util.warn)('Type3 character \"' + glyph.operatorListId + '\" is not available'); + (0, _util.warn)(`Type3 character "${glyph.operatorListId}" is not available.`); continue; } this.processingType3 = glyph; @@ -6708,8 +6704,8 @@ exports.TilingPattern = TilingPattern; "use strict"; -var pdfjsVersion = '1.8.290'; -var pdfjsBuild = '60c232bc'; +var pdfjsVersion = '1.8.314'; +var pdfjsBuild = '3adda80f'; var pdfjsSharedUtil = __w_pdfjs_require__(0); var pdfjsDisplayGlobal = __w_pdfjs_require__(8); var pdfjsDisplayAPI = __w_pdfjs_require__(3); @@ -6721,6 +6717,7 @@ exports.PDFJS = pdfjsDisplayGlobal.PDFJS; exports.build = pdfjsDisplayAPI.build; exports.version = pdfjsDisplayAPI.version; exports.getDocument = pdfjsDisplayAPI.getDocument; +exports.LoobpackPort = pdfjsDisplayAPI.LoopbackPort; exports.PDFDataRangeTransport = pdfjsDisplayAPI.PDFDataRangeTransport; exports.PDFWorker = pdfjsDisplayAPI.PDFWorker; exports.renderTextLayer = pdfjsDisplayTextLayer.renderTextLayer; diff --git a/browser/extensions/pdfjs/content/build/pdf.worker.js b/browser/extensions/pdfjs/content/build/pdf.worker.js index 181092d84b40a..f27b9b2dedbea 100644 --- a/browser/extensions/pdfjs/content/build/pdf.worker.js +++ b/browser/extensions/pdfjs/content/build/pdf.worker.js @@ -992,7 +992,7 @@ function MessageHandler(sourceName, targetName, comObj) { this.postMessageTransfers = true; var callbacksCapabilities = this.callbacksCapabilities = Object.create(null); var ah = this.actionHandler = Object.create(null); - this._onComObjOnMessage = function messageHandlerComObjOnMessage(event) { + this._onComObjOnMessage = event => { var data = event.data; if (data.targetName !== this.sourceName) { return; @@ -1043,7 +1043,7 @@ function MessageHandler(sourceName, targetName, comObj) { } else { error('Unknown action from worker: ' + data.action); } - }.bind(this); + }; comObj.addEventListener('message', this._onComObjOnMessage); } MessageHandler.prototype = { @@ -4669,9 +4669,13 @@ var Lexer = function LexerClosure() { divideBy = 10; ch = this.nextChar(); } + if (ch === 0x0A || ch === 0x0D) { + do { + ch = this.nextChar(); + } while (ch === 0x0A || ch === 0x0D); + } if (ch < 0x30 || ch > 0x39) { - error('Invalid number: ' + String.fromCharCode(ch)); - return 0; + error(`Invalid number: ${String.fromCharCode(ch)} (charCode ${ch})`); } var baseValue = ch - 0x30; var powerValue = 0; @@ -14556,7 +14560,7 @@ var ChunkedStreamManager = function ChunkedStreamManagerClosure() { }; rangeReader.read().then(readChunk, reject); }); - promise.then(function (data) { + promise.then(data => { if (this.aborted) { return; } @@ -14564,7 +14568,7 @@ var ChunkedStreamManager = function ChunkedStreamManagerClosure() { chunk: data, begin }); - }.bind(this)); + }); }, requestAllChunks: function ChunkedStreamManager_requestAllChunks() { var missingChunks = this.stream.getMissingChunks(); @@ -16436,7 +16440,7 @@ var reverseIfRtl = coreUnicode.reverseIfRtl; var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph; var getGlyphsUnicode = coreGlyphList.getGlyphsUnicode; var PartialEvaluator = function PartialEvaluatorClosure() { - var DefaultPartialEvaluatorOptions = { + const DefaultPartialEvaluatorOptions = { forceDataSchema: false, maxImageSize: -1, disableFontFace: false, @@ -16481,7 +16485,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var cs = ColorSpace.parse(dict.get('ColorSpace', 'CS'), xref, res); return (cs.numComps === 1 || cs.numComps === 3) && cs.isDefaultDecode(dict.getArray('Decode', 'D')); }; - function PartialEvaluator(pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options) { + function PartialEvaluator({ pdfManager, xref, handler, pageIndex, idFactory, fontCache, builtInCMapCache, options = null }) { this.pdfManager = pdfManager; this.xref = xref; this.handler = handler; @@ -16495,7 +16499,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() { if (cachedCMap) { return Promise.resolve(cachedCMap); } - return handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => { + return this.handler.sendWithPromise('FetchBuiltInCMap', { name }).then(data => { if (data.compressionType !== CMapCompressionType.NONE) { this.builtInCMapCache[name] = data; } @@ -16658,7 +16662,13 @@ var PartialEvaluator = function PartialEvaluatorClosure() { operatorList.addOp(OPS.beginGroup, [groupOptions]); } operatorList.addOp(OPS.paintFormXObjectBegin, [matrix, bbox]); - return this.getOperatorList(xobj, task, dict.get('Resources') || resources, operatorList, initialState).then(function () { + return this.getOperatorList({ + stream: xobj, + task, + resources: dict.get('Resources') || resources, + operatorList, + initialState + }).then(function () { operatorList.addOp(OPS.paintFormXObjectEnd, []); if (group) { operatorList.addOp(OPS.endGroup, [groupOptions]); @@ -16666,7 +16676,6 @@ var PartialEvaluator = function PartialEvaluatorClosure() { }); }, buildPaintImageXObject: function PartialEvaluator_buildPaintImageXObject(resources, image, inline, operatorList, cacheKey, imageCache) { - var self = this; var dict = image.dict; var w = dict.get('Width', 'W'); var h = dict.get('Height', 'H'); @@ -16720,14 +16729,14 @@ var PartialEvaluator = function PartialEvaluatorClosure() { } var nativeImageDecoder = null; if (useNativeImageDecoder && (image instanceof JpegStream || mask instanceof JpegStream || softMask instanceof JpegStream)) { - nativeImageDecoder = new NativeImageDecoder(self.xref, resources, self.handler, self.options.forceDataSchema); + nativeImageDecoder = new NativeImageDecoder(this.xref, resources, this.handler, this.options.forceDataSchema); } - PDFImage.buildImage(self.handler, self.xref, resources, image, inline, nativeImageDecoder).then(function (imageObj) { + PDFImage.buildImage(this.handler, this.xref, resources, image, inline, nativeImageDecoder).then(imageObj => { var imgData = imageObj.createImageData(false); - self.handler.send('obj', [objId, self.pageIndex, 'Image', imgData], [imgData.data.buffer]); - }).then(undefined, function (reason) { + this.handler.send('obj', [objId, this.pageIndex, 'Image', imgData], [imgData.data.buffer]); + }).catch(reason => { warn('Unable to decode image: ' + reason); - self.handler.send('obj', [objId, self.pageIndex, 'Image', null]); + this.handler.send('obj', [objId, this.pageIndex, 'Image', null]); }); operatorList.addOp(OPS.paintImageXObject, args); if (cacheKey) { @@ -16761,7 +16770,12 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var tilingOpList = new OperatorList(); var resourcesArray = [patternDict.get('Resources'), resources]; var patternResources = Dict.merge(this.xref, resourcesArray); - return this.getOperatorList(pattern, task, patternResources, tilingOpList).then(function () { + return this.getOperatorList({ + stream: pattern, + task, + resources: patternResources, + operatorList: tilingOpList + }).then(function () { operatorList.addDependencies(tilingOpList.dependencies); operatorList.addOp(fn, getTilingPatternIR({ fnArray: tilingOpList.fnArray, @@ -16775,20 +16789,19 @@ var PartialEvaluator = function PartialEvaluatorClosure() { fontArgs = fontArgs.slice(); fontName = fontArgs[0].name; } - var self = this; - return this.loadFont(fontName, fontRef, resources).then(function (translated) { + return this.loadFont(fontName, fontRef, resources).then(translated => { if (!translated.font.isType3Font) { return translated; } - return translated.loadType3Data(self, resources, operatorList, task).then(function () { + return translated.loadType3Data(this, resources, operatorList, task).then(function () { return translated; - }, function (reason) { - self.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font }); + }).catch(reason => { + this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font }); return new TranslatedFont('g_font_error', new ErrorFont('Type3 font load error: ' + reason), translated.font); }); - }).then(function (translated) { + }).then(translated => { state.font = translated.font; - translated.send(self.handler); + translated.send(this.handler); return translated.loadedName; }); }, @@ -16797,12 +16810,12 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var glyphs = font.charsToGlyphs(chars); var isAddToPathSet = !!(state.textRenderingMode & TextRenderingMode.ADD_TO_PATH_FLAG); if (font.data && (isAddToPathSet || this.options.disableFontFace)) { - var buildPath = function (fontChar) { + var buildPath = fontChar => { if (!font.renderer.hasBuiltPath(fontChar)) { var path = font.renderer.getPathJs(fontChar); this.handler.send('commonobj', [font.loadedName + '_path_' + fontChar, 'FontPath', path]); } - }.bind(this); + }; for (var i = 0, ii = glyphs.length; i < ii; i++) { var glyph = glyphs[i]; buildPath(glyph.fontChar); @@ -16817,11 +16830,10 @@ var PartialEvaluator = function PartialEvaluatorClosure() { setGState: function PartialEvaluator_setGState(resources, gState, operatorList, task, stateManager) { var gStateObj = []; var gStateKeys = gState.getKeys(); - var self = this; var promise = Promise.resolve(); for (var i = 0, ii = gStateKeys.length; i < ii; i++) { - var key = gStateKeys[i]; - var value = gState.get(key); + let key = gStateKeys[i]; + let value = gState.get(key); switch (key) { case 'Type': break; @@ -16837,8 +16849,8 @@ var PartialEvaluator = function PartialEvaluatorClosure() { gStateObj.push([key, value]); break; case 'Font': - promise = promise.then(function () { - return self.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) { + promise = promise.then(() => { + return this.handleSetFont(resources, null, value[0], operatorList, task, stateManager.state).then(function (loadedName) { operatorList.addDependency(loadedName); gStateObj.push([key, [loadedName, value[1]]]); }); @@ -16853,9 +16865,9 @@ var PartialEvaluator = function PartialEvaluatorClosure() { break; } if (isDict(value)) { - promise = promise.then(function (dict) { - return self.handleSMask(dict, resources, operatorList, task, stateManager); - }.bind(this, value)); + promise = promise.then(() => { + return this.handleSMask(value, resources, operatorList, task, stateManager); + }); gStateObj.push([key, true]); } else { warn('Unsupported SMask type'); @@ -16965,15 +16977,14 @@ var PartialEvaluator = function PartialEvaluatorClosure() { } catch (e) { translatedPromise = Promise.reject(e); } - var self = this; translatedPromise.then(function (translatedFont) { if (translatedFont.fontType !== undefined) { var xrefFontStats = xref.stats.fontTypes; xrefFontStats[translatedFont.fontType] = true; } fontCapability.resolve(new TranslatedFont(font.loadedName, translatedFont, font)); - }, function (reason) { - self.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font }); + }).catch(reason => { + this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.font }); try { var descriptor = preEvaluatedFont.descriptor; var fontFile3 = descriptor && descriptor.get('FontFile3'); @@ -17020,15 +17031,16 @@ var PartialEvaluator = function PartialEvaluatorClosure() { operatorList.addOp(fn, args); return Promise.resolve(); }, - getOperatorList: function PartialEvaluator_getOperatorList(stream, task, resources, operatorList, initialState) { + getOperatorList({ stream, task, resources, operatorList, initialState = null }) { + resources = resources || Dict.empty; + initialState = initialState || new EvalState(); + assert(operatorList, 'getOperatorList: missing "operatorList" parameter'); var self = this; var xref = this.xref; var imageCache = Object.create(null); - assert(operatorList); - resources = resources || Dict.empty; var xobjs = resources.get('XObject') || Dict.empty; var patterns = resources.get('Pattern') || Dict.empty; - var stateManager = new StateManager(initialState || new EvalState()); + var stateManager = new StateManager(initialState); var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); var timeSlotManager = new TimeSlotManager(); function closePendingRestoreOPS(argument) { @@ -17273,7 +17285,7 @@ var PartialEvaluator = function PartialEvaluatorClosure() { } closePendingRestoreOPS(); resolve(); - }).catch(function (reason) { + }).catch(reason => { if (this.options.ignoreErrors) { this.handler.send('UnsupportedFeature', { featureId: UNSUPPORTED_FEATURES.unknown }); warn('getOperatorList - ignoring errors during task: ' + task.name); @@ -17281,9 +17293,10 @@ var PartialEvaluator = function PartialEvaluatorClosure() { return; } throw reason; - }.bind(this)); + }); }, - getTextContent: function PartialEvaluator_getTextContent(stream, task, resources, stateManager, normalizeWhitespace, combineTextItems) { + getTextContent({ stream, task, resources, stateManager = null, normalizeWhitespace = false, combineTextItems = false }) { + resources = resources || Dict.empty; stateManager = stateManager || new StateManager(new TextState()); var WhitespaceRegexp = /\s/g; var textContent = { @@ -17312,7 +17325,6 @@ var PartialEvaluator = function PartialEvaluatorClosure() { var MULTI_SPACE_FACTOR_MAX = 4; var self = this; var xref = this.xref; - resources = xref.fetchIfRef(resources) || Dict.empty; var xobjs = null; var xobjsCache = Object.create(null); var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager); @@ -17660,7 +17672,14 @@ var PartialEvaluator = function PartialEvaluatorClosure() { if (isArray(matrix) && matrix.length === 6) { xObjStateManager.transform(matrix); } - next(self.getTextContent(xobj, task, xobj.dict.get('Resources') || resources, xObjStateManager, normalizeWhitespace, combineTextItems).then(function (formTextContent) { + next(self.getTextContent({ + stream: xobj, + task, + resources: xobj.dict.get('Resources') || resources, + stateManager: xObjStateManager, + normalizeWhitespace, + combineTextItems + }).then(function (formTextContent) { Util.appendToArray(textContent.items, formTextContent.items); Util.extendObj(textContent.styles, formTextContent.styles); xobjsCache.key = name; @@ -17694,14 +17713,14 @@ var PartialEvaluator = function PartialEvaluatorClosure() { } flushTextContentItem(); resolve(textContent); - }).catch(function (reason) { + }).catch(reason => { if (this.options.ignoreErrors) { warn('getTextContent - ignoring errors during task: ' + task.name); flushTextContentItem(); return textContent; } throw reason; - }.bind(this)); + }); }, extractDataStructures: function PartialEvaluator_extractDataStructures(dict, baseDict, properties) { var xref = this.xref; @@ -17777,10 +17796,10 @@ var PartialEvaluator = function PartialEvaluatorClosure() { properties.baseEncodingName = baseEncodingName; properties.hasEncoding = !!baseEncodingName || differences.length > 0; properties.dict = dict; - return toUnicodePromise.then(function (toUnicode) { + return toUnicodePromise.then(toUnicode => { properties.toUnicode = toUnicode; return this.buildToUnicode(properties); - }.bind(this)).then(function (toUnicode) { + }).then(function (toUnicode) { properties.toUnicode = toUnicode; return properties; }); @@ -18160,10 +18179,10 @@ var PartialEvaluator = function PartialEvaluatorClosure() { firstChar: 0, lastChar: maxCharIndex }; - return this.extractDataStructures(dict, dict, properties).then(function (properties) { + return this.extractDataStructures(dict, dict, properties).then(properties => { properties.widths = this.buildCharCodeToWidth(metrics.widths, properties); return new Font(baseFontName, null, properties); - }.bind(this)); + }); } } var firstChar = dict.get('FirstChar') || 0; @@ -18241,15 +18260,15 @@ var PartialEvaluator = function PartialEvaluatorClosure() { } else { cMapPromise = Promise.resolve(undefined); } - return cMapPromise.then(function () { + return cMapPromise.then(() => { return this.extractDataStructures(dict, baseDict, properties); - }.bind(this)).then(function (properties) { + }).then(properties => { this.extractWidths(dict, descriptor, properties); if (type === 'Type3') { properties.isType3Font = true; } return new Font(fontName.name, fontFile, properties); - }.bind(this)); + }); } }; return PartialEvaluator; @@ -18286,18 +18305,24 @@ var TranslatedFont = function TranslatedFontClosure() { var charProcKeys = charProcs.getKeys(); var charProcOperatorList = Object.create(null); for (var i = 0, n = charProcKeys.length; i < n; ++i) { - loadCharProcsPromise = loadCharProcsPromise.then(function (key) { + let key = charProcKeys[i]; + loadCharProcsPromise = loadCharProcsPromise.then(function () { var glyphStream = charProcs.get(key); var operatorList = new OperatorList(); - return type3Evaluator.getOperatorList(glyphStream, task, fontResources, operatorList).then(function () { + return type3Evaluator.getOperatorList({ + stream: glyphStream, + task, + resources: fontResources, + operatorList + }).then(function () { charProcOperatorList[key] = operatorList.getIR(); parentOperatorList.addDependencies(operatorList.dependencies); - }, function (reason) { - warn('Type3 font resource \"' + key + '\" is not available'); + }).catch(function (reason) { + warn(`Type3 font resource "${key}" is not available.`); var operatorList = new OperatorList(); charProcOperatorList[key] = operatorList.getIR(); }); - }.bind(this, charProcKeys[i])); + }); } this.type3Loaded = loadCharProcsPromise.then(function () { translatedFont.charProcOperatorList = charProcOperatorList; @@ -21604,22 +21629,20 @@ var Catalog = function CatalogClosure() { this.fontCache.forEach(function (promise) { promises.push(promise); }); - return Promise.all(promises).then(function (translatedFonts) { + return Promise.all(promises).then(translatedFonts => { for (var i = 0, ii = translatedFonts.length; i < ii; i++) { var font = translatedFonts[i].dict; delete font.translated; } this.fontCache.clear(); this.builtInCMapCache = Object.create(null); - }.bind(this)); + }); }, getPage: function Catalog_getPage(pageIndex) { if (!(pageIndex in this.pagePromises)) { - this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(function (a) { - var dict = a[0]; - var ref = a[1]; + this.pagePromises[pageIndex] = this.getPageDict(pageIndex).then(([dict, ref]) => { return this.pageFactory.createPage(pageIndex, dict, ref, this.fontCache, this.builtInCMapCache); - }.bind(this)); + }); } return this.pagePromises[pageIndex]; }, @@ -22664,7 +22687,7 @@ var ObjectLoader = function () { addChildren(currentNode, nodesToVisit); } if (pendingRequests.length) { - this.xref.stream.manager.requestRanges(pendingRequests).then(function pendingRequestCallback() { + this.xref.stream.manager.requestRanges(pendingRequests).then(() => { nodesToVisit = nodesToRevisit; for (var i = 0; i < nodesToRevisit.length; i++) { var node = nodesToRevisit[i]; @@ -22673,7 +22696,7 @@ var ObjectLoader = function () { } } this._walk(nodesToVisit); - }.bind(this), this.capability.reject); + }, this.capability.reject); return; } this.refSet = null; @@ -23596,7 +23619,7 @@ function setPDFNetworkStreamClass(cls) { PDFNetworkStream = cls; } var WorkerMessageHandler = { - setup: function wphSetup(handler, port) { + setup(handler, port) { var testMessageProcessed = false; handler.on('test', function wphSetupTest(data) { if (testMessageProcessed) { @@ -23632,7 +23655,7 @@ var WorkerMessageHandler = { return WorkerMessageHandler.createDocumentHandler(data, port); }); }, - createDocumentHandler: function wphCreateDocumentHandler(docParams, port) { + createDocumentHandler(docParams, port) { var pdfManager; var terminated = false; var cancelXHRs = null; @@ -23913,7 +23936,12 @@ var WorkerMessageHandler = { startWorkerTask(task); var pageNum = pageIndex + 1; var start = Date.now(); - page.getOperatorList(handler, task, data.intent, data.renderInteractiveForms).then(function (operatorList) { + page.getOperatorList({ + handler, + task, + intent: data.intent, + renderInteractiveForms: data.renderInteractiveForms + }).then(function (operatorList) { finishWorkerTask(task); info('page=' + pageNum + ' - getOperatorList: time=' + (Date.now() - start) + 'ms, len=' + operatorList.totalLength); }, function (e) { @@ -23955,7 +23983,12 @@ var WorkerMessageHandler = { startWorkerTask(task); var pageNum = pageIndex + 1; var start = Date.now(); - return page.extractTextContent(handler, task, data.normalizeWhitespace, data.combineTextItems).then(function (textContent) { + return page.extractTextContent({ + handler, + task, + normalizeWhitespace: data.normalizeWhitespace, + combineTextItems: data.combineTextItems + }).then(function (textContent) { finishWorkerTask(task); info('text indexing: page=' + pageNum + ' - time=' + (Date.now() - start) + 'ms'); return textContent; @@ -23995,15 +24028,18 @@ var WorkerMessageHandler = { docParams = null; }); return workerHandlerName; + }, + initializeFromPort(port) { + var handler = new MessageHandler('worker', 'main', port); + WorkerMessageHandler.setup(handler, port); + handler.send('ready', null); } }; -function initializeWorker() { - var handler = new MessageHandler('worker', 'main', self); - WorkerMessageHandler.setup(handler, self); - handler.send('ready', null); +function isMessagePort(maybePort) { + return typeof maybePort.postMessage === 'function' && 'onmessage' in maybePort; } -if (typeof window === 'undefined' && !isNodeJS()) { - initializeWorker(); +if (typeof window === 'undefined' && !isNodeJS() && typeof self !== 'undefined' && isMessagePort(self)) { + WorkerMessageHandler.initializeFromPort(self); } exports.setPDFNetworkStreamClass = setPDFNetworkStreamClass; exports.WorkerTask = WorkerTask; @@ -24270,18 +24306,15 @@ var Annotation = function AnnotationClosure() { this.data.contents = stringToPDFString(dict.get('Contents') || ''); }, loadResources: function Annotation_loadResources(keys) { - return new Promise(function (resolve, reject) { - this.appearance.dict.getAsync('Resources').then(function (resources) { - if (!resources) { - resolve(); - return; - } - var objectLoader = new ObjectLoader(resources.map, keys, resources.xref); - objectLoader.load().then(function () { - resolve(resources); - }, reject); - }, reject); - }.bind(this)); + return this.appearance.dict.getAsync('Resources').then(resources => { + if (!resources) { + return; + } + var objectLoader = new ObjectLoader(resources.map, keys, resources.xref); + return objectLoader.load().then(function () { + return resources; + }); + }); }, getOperatorList: function Annotation_getOperatorList(evaluator, task, renderForms) { if (!this.appearance) { @@ -24293,13 +24326,17 @@ var Annotation = function AnnotationClosure() { var bbox = appearanceDict.getArray('BBox') || [0, 0, 1, 1]; var matrix = appearanceDict.getArray('Matrix') || [1, 0, 0, 1, 0, 0]; var transform = getTransformMatrix(data.rect, bbox, matrix); - var self = this; - return resourcesPromise.then(function (resources) { + return resourcesPromise.then(resources => { var opList = new OperatorList(); opList.addOp(OPS.beginAnnotation, [data.rect, transform, matrix]); - return evaluator.getOperatorList(self.appearance, task, resources, opList).then(function () { + return evaluator.getOperatorList({ + stream: this.appearance, + task, + resources, + operatorList: opList + }).then(() => { opList.addOp(OPS.endAnnotation, []); - self.appearance.reset(); + this.appearance.reset(); return opList; }); }); @@ -24464,7 +24501,12 @@ var TextWidgetAnnotation = function TextWidgetAnnotationClosure() { return Promise.resolve(operatorList); } var stream = new Stream(stringToBytes(this.data.defaultAppearance)); - return evaluator.getOperatorList(stream, task, this.fieldResources, operatorList).then(function () { + return evaluator.getOperatorList({ + stream, + task, + resources: this.fieldResources, + operatorList + }).then(function () { return operatorList; }); } @@ -25810,34 +25852,43 @@ var Page = function PageClosure() { if (!this.resourcesPromise) { this.resourcesPromise = this.pdfManager.ensure(this, 'resources'); } - return this.resourcesPromise.then(function resourceSuccess() { + return this.resourcesPromise.then(() => { var objectLoader = new ObjectLoader(this.resources.map, keys, this.xref); return objectLoader.load(); - }.bind(this)); + }); }, - getOperatorList: function Page_getOperatorList(handler, task, intent, renderInteractiveForms) { - var self = this; - var pdfManager = this.pdfManager; - var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []); + getOperatorList({ handler, task, intent, renderInteractiveForms }) { + var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream'); var resourcesPromise = this.loadResources(['ExtGState', 'ColorSpace', 'Pattern', 'Shading', 'XObject', 'Font']); - var partialEvaluator = new PartialEvaluator(pdfManager, this.xref, handler, this.pageIndex, this.idFactory, this.fontCache, this.builtInCMapCache, this.evaluatorOptions); + var partialEvaluator = new PartialEvaluator({ + pdfManager: this.pdfManager, + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this.idFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + options: this.evaluatorOptions + }); var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]); - var pageListPromise = dataPromises.then(function (data) { - var contentStream = data[0]; - var opList = new OperatorList(intent, handler, self.pageIndex); + var pageListPromise = dataPromises.then(([contentStream]) => { + var opList = new OperatorList(intent, handler, this.pageIndex); handler.send('StartRenderPage', { - transparency: partialEvaluator.hasBlendModes(self.resources), - pageIndex: self.pageIndex, + transparency: partialEvaluator.hasBlendModes(this.resources), + pageIndex: this.pageIndex, intent }); - return partialEvaluator.getOperatorList(contentStream, task, self.resources, opList).then(function () { + return partialEvaluator.getOperatorList({ + stream: contentStream, + task, + resources: this.resources, + operatorList: opList + }).then(function () { return opList; }); }); - var annotationsPromise = pdfManager.ensure(this, 'annotations'); - return Promise.all([pageListPromise, annotationsPromise]).then(function (datas) { - var pageOpList = datas[0]; - var annotations = datas[1]; + var annotationsPromise = this.pdfManager.ensure(this, 'annotations'); + return Promise.all([pageListPromise, annotationsPromise]).then(function ([pageOpList, annotations]) { if (annotations.length === 0) { pageOpList.flush(true); return pageOpList; @@ -25861,16 +25912,28 @@ var Page = function PageClosure() { }); }); }, - extractTextContent: function Page_extractTextContent(handler, task, normalizeWhitespace, combineTextItems) { - var self = this; - var pdfManager = this.pdfManager; - var contentStreamPromise = pdfManager.ensure(this, 'getContentStream', []); + extractTextContent({ handler, task, normalizeWhitespace, combineTextItems }) { + var contentStreamPromise = this.pdfManager.ensure(this, 'getContentStream'); var resourcesPromise = this.loadResources(['ExtGState', 'XObject', 'Font']); var dataPromises = Promise.all([contentStreamPromise, resourcesPromise]); - return dataPromises.then(function (data) { - var contentStream = data[0]; - var partialEvaluator = new PartialEvaluator(pdfManager, self.xref, handler, self.pageIndex, self.idFactory, self.fontCache, self.builtInCMapCache, self.evaluatorOptions); - return partialEvaluator.getTextContent(contentStream, task, self.resources, null, normalizeWhitespace, combineTextItems); + return dataPromises.then(([contentStream]) => { + var partialEvaluator = new PartialEvaluator({ + pdfManager: this.pdfManager, + xref: this.xref, + handler, + pageIndex: this.pageIndex, + idFactory: this.idFactory, + fontCache: this.fontCache, + builtInCMapCache: this.builtInCMapCache, + options: this.evaluatorOptions + }); + return partialEvaluator.getTextContent({ + stream: contentStream, + task, + resources: this.resources, + normalizeWhitespace, + combineTextItems + }); }); }, getAnnotationsData: function Page_getAnnotationsData(intent) { @@ -27293,7 +27356,7 @@ var Font = function FontClosure() { this.toFontChar = buildToFontChar(properties.defaultEncoding, getGlyphsUnicode(), properties.differences); } else { glyphsUnicodeMap = getGlyphsUnicode(); - this.toUnicode.forEach(function (charCode, unicodeCharCode) { + this.toUnicode.forEach((charCode, unicodeCharCode) => { if (!this.composite) { glyphName = properties.differences[charCode] || properties.defaultEncoding[charCode]; unicode = getUnicodeForGlyph(glyphName, glyphsUnicodeMap); @@ -27302,7 +27365,7 @@ var Font = function FontClosure() { } } this.toFontChar[charCode] = unicodeCharCode; - }.bind(this)); + }); } this.loadedName = fontName.split('-')[0]; this.loading = false; @@ -36563,8 +36626,8 @@ exports.Type1Parser = Type1Parser; "use strict"; -var pdfjsVersion = '1.8.290'; -var pdfjsBuild = '60c232bc'; +var pdfjsVersion = '1.8.314'; +var pdfjsBuild = '3adda80f'; var pdfjsCoreWorker = __w_pdfjs_require__(17); ; exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler; From 3feaac0370f0e65fda3d7f75586f1e7c18d6ac2e Mon Sep 17 00:00:00 2001 From: Justin Wood Date: Thu, 4 May 2017 12:51:06 -0400 Subject: [PATCH 087/131] Backout Bug 1337861 (Enforce MOZ_BUILD_DATE) due to Bug 1360550. r=catlee a=catlee Bug 1360550 resulted in the buildid the Linux builds had being different than the directory they were uploaded to. This had fallout affects for QA's firefox-ui tests and presumably anything using mozdownload. MozReview-Commit-ID: 7xbEihhDF1N --- .../mozharness/mozilla/building/buildbase.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/testing/mozharness/mozharness/mozilla/building/buildbase.py b/testing/mozharness/mozharness/mozilla/building/buildbase.py index 379c56b4d5820..e8c5c8870bbb9 100755 --- a/testing/mozharness/mozharness/mozilla/building/buildbase.py +++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py @@ -796,27 +796,16 @@ def query_buildid(self): buildid = None if c.get("is_automation"): - if self.buildbot_config.get('properties'): - # We're on buildbot - if self.buildbot_config.get('properties').get('buildid'): - # Try may not provide a buildid. This means, it's gonna be generated - # below. - self.info("Determining buildid from buildbot properties") - buildid = self.buildbot_config['properties']['buildid'].encode( - 'ascii', 'replace' - ) + if self.buildbot_config['properties'].get('buildid'): + self.info("Determining buildid from buildbot properties") + buildid = self.buildbot_config['properties']['buildid'].encode( + 'ascii', 'replace' + ) else: - # We're on taskcluster. - # In this case, there are no buildbot properties, and we must pass + # for taskcluster, there are no buildbot properties, and we pass # MOZ_BUILD_DATE into mozharness as an environment variable, only # to have it pass the same value out with the same name. - try: - buildid = os.environ['MOZ_BUILD_DATE'] - except KeyError: - self.fatal( - "MOZ_BUILD_DATE must be provided as an environment var on Taskcluster" - ) - + buildid = os.environ.get('MOZ_BUILD_DATE') if not buildid: self.info("Creating buildid through current time") From ab439c20171d9b5f21489927b0b999f55ff7d537 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 4 May 2017 18:06:58 +0100 Subject: [PATCH 088/131] Bug 1360526 - Make weak cache iteration more readable r=sfink --- js/src/jsgc.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 1b1a5a3c183e2..5c3d6853ebdb6 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -5167,7 +5167,7 @@ GCRuntime::sweepJitDataOnMainThread(FreeOp* fop) using WeakCacheTaskVector = mozilla::Vector; template -static bool +static inline bool IterateWeakCaches(JSRuntime* rt, Functor f) { for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { @@ -5189,19 +5189,22 @@ static WeakCacheTaskVector PrepareWeakCacheTasks(JSRuntime* rt) { // Build a vector of sweep tasks to run on a helper thread. - WeakCacheTaskVector out; - if (IterateWeakCaches(rt, [&] (JS::WeakCache* cache) { - return out.emplaceBack(rt, *cache); - })) { - return out; + WeakCacheTaskVector tasks; + bool ok = IterateWeakCaches(rt, [&] (JS::WeakCache* cache) { + return tasks.emplaceBack(rt, *cache); + }); + + // If we ran out of memory, do all the work now and ensure we return an + // empty list. + if (!ok) { + IterateWeakCaches(rt, [&] (JS::WeakCache* cache) { + SweepWeakCacheTask(rt, *cache).runFromActiveCooperatingThread(rt); + return true; + }); + tasks.clear(); } - // If we ran out of memory, do all the work now and return an empty list. - IterateWeakCaches(rt, [&] (JS::WeakCache* cache) { - SweepWeakCacheTask(rt, *cache).runFromActiveCooperatingThread(rt); - return true; - }); - return WeakCacheTaskVector(); + return tasks; } template From e6ed0440815c7c87d34b5163ed06801999131389 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Thu, 4 May 2017 18:06:59 +0100 Subject: [PATCH 089/131] Bug 1360526 - Refactor to remove GCSweepTask and associated macros r=sfink --- js/src/gc/GCRuntime.h | 6 +- js/src/jsgc.cpp | 179 ++++++++++++++++++++---------------------- 2 files changed, 86 insertions(+), 99 deletions(-) diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index ae14116a0cb99..a7937c353d33b 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -33,7 +33,7 @@ typedef Vector ZoneGroupVector; using BlackGrayEdgeVector = Vector; class AutoMaybeStartBackgroundAllocation; -template class AutoRunGCSweepTask; +class AutoRunParallelTask; class AutoTraceSession; class MarkingValidator; struct MovingTracer; @@ -987,7 +987,7 @@ class GCRuntime MOZ_MUST_USE bool findInterZoneEdges(); void getNextSweepGroup(); void endMarkingSweepGroup(); - void beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock); + void beginSweepingSweepGroup(); bool shouldReleaseObservedTypes(); void sweepDebuggerOnMainThread(FreeOp* fop); void sweepJitDataOnMainThread(FreeOp* fop); @@ -1230,7 +1230,7 @@ class GCRuntime */ void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); - template friend class AutoRunGCSweepTask; + friend class AutoRunParallelTask; /* * List head of arenas allocated during the sweep phase. diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5c3d6853ebdb6..0f82fcb4aa2f5 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -4928,28 +4928,20 @@ GCRuntime::endMarkingSweepGroup() marker.setMarkColorBlack(); } -class GCSweepTask : public GCParallelTask -{ - GCSweepTask(const GCSweepTask&) = delete; - - public: - explicit GCSweepTask(JSRuntime* rt) : GCParallelTask(rt) {} - GCSweepTask(GCSweepTask&& other) - : GCParallelTask(mozilla::Move(other)) - {} -}; - // Causes the given WeakCache to be swept when run. -class SweepWeakCacheTask : public GCSweepTask +class SweepWeakCacheTask : public GCParallelTask { JS::WeakCache& cache; SweepWeakCacheTask(const SweepWeakCacheTask&) = delete; public: - SweepWeakCacheTask(JSRuntime* rt, JS::WeakCache& wc) : GCSweepTask(rt), cache(wc) {} + SweepWeakCacheTask(JSRuntime* rt, JS::WeakCache& wc) + : GCParallelTask(rt), cache(wc) + {} + SweepWeakCacheTask(SweepWeakCacheTask&& other) - : GCSweepTask(mozilla::Move(other)), cache(other.cache) + : GCParallelTask(mozilla::Move(other)), cache(other.cache) {} void run() override { @@ -4957,68 +4949,51 @@ class SweepWeakCacheTask : public GCSweepTask } }; -#define MAKE_GC_SWEEP_TASK(name, phase) \ - class name : public GCSweepTask { \ - void run() override; \ - public: \ - explicit name (JSRuntime* rt) : GCSweepTask(rt) {} \ - static const gcstats::Phase StatsPhase = phase; \ - } -MAKE_GC_SWEEP_TASK(SweepAtomsTask, gcstats::PHASE_SWEEP_ATOMS); -MAKE_GC_SWEEP_TASK(SweepCCWrappersTask, gcstats::PHASE_SWEEP_CC_WRAPPER); -MAKE_GC_SWEEP_TASK(SweepObjectGroupsTask, gcstats::PHASE_SWEEP_TYPE_OBJECT); -MAKE_GC_SWEEP_TASK(SweepRegExpsTask, gcstats::PHASE_SWEEP_REGEXP); -MAKE_GC_SWEEP_TASK(SweepMiscTask, gcstats::PHASE_SWEEP_MISC); -MAKE_GC_SWEEP_TASK(SweepCompressionTasksTask, gcstats::PHASE_SWEEP_COMPRESSION); -MAKE_GC_SWEEP_TASK(SweepWeakMapsTask, gcstats::PHASE_SWEEP_WEAKMAPS); -MAKE_GC_SWEEP_TASK(SweepUniqueIdsTask, gcstats::PHASE_SWEEP_UNIQUEIDS); -#undef MAKE_GC_SWEEP_TASK - -/* virtual */ void -SweepAtomsTask::run() +static void +SweepAtoms(JSRuntime* runtime) { DenseBitmap marked; - if (runtime()->gc.atomMarking.computeBitmapFromChunkMarkBits(runtime(), marked)) { - for (GCZonesIter zone(runtime()); !zone.done(); zone.next()) - runtime()->gc.atomMarking.updateZoneBitmap(zone, marked); + if (runtime->gc.atomMarking.computeBitmapFromChunkMarkBits(runtime, marked)) { + for (GCZonesIter zone(runtime); !zone.done(); zone.next()) + runtime->gc.atomMarking.updateZoneBitmap(zone, marked); } else { // Ignore OOM in computeBitmapFromChunkMarkBits. The updateZoneBitmap // call can only remove atoms from the zone bitmap, so it is // conservative to just not call it. } - runtime()->gc.atomMarking.updateChunkMarkBits(runtime()); - runtime()->sweepAtoms(); - runtime()->unsafeSymbolRegistry().sweep(); - for (CompartmentsIter comp(runtime(), SkipAtoms); !comp.done(); comp.next()) + runtime->gc.atomMarking.updateChunkMarkBits(runtime); + runtime->sweepAtoms(); + runtime->unsafeSymbolRegistry().sweep(); + for (CompartmentsIter comp(runtime, SkipAtoms); !comp.done(); comp.next()) comp->sweepVarNames(); } -/* virtual */ void -SweepCCWrappersTask::run() +static void +SweepCCWrappers(JSRuntime* runtime) { - for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) + for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) c->sweepCrossCompartmentWrappers(); } -/* virtual */ void -SweepObjectGroupsTask::run() +static void +SweepObjectGroups(JSRuntime* runtime) { - for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) - c->objectGroups.sweep(runtime()->defaultFreeOp()); + for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) + c->objectGroups.sweep(runtime->defaultFreeOp()); } -/* virtual */ void -SweepRegExpsTask::run() +static void +SweepRegExps(JSRuntime* runtime) { - for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) + for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) c->sweepRegExps(); } -/* virtual */ void -SweepMiscTask::run() +static void +SweepMisc(JSRuntime* runtime) { - for (GCCompartmentGroupIter c(runtime()); !c.done(); c.next()) { + for (GCCompartmentGroupIter c(runtime); !c.done(); c.next()) { c->sweepGlobalObject(); c->sweepTemplateObjects(); c->sweepSavedStacks(); @@ -5029,15 +5004,15 @@ SweepMiscTask::run() } } -/* virtual */ void -SweepCompressionTasksTask::run() +static void +SweepCompressionTasks(JSRuntime* runtime) { AutoLockHelperThreadState lock; // Attach finished compression tasks. auto& finished = HelperThreadState().compressionFinishedList(lock); for (size_t i = 0; i < finished.length(); i++) { - if (finished[i]->runtimeMatches(runtime())) { + if (finished[i]->runtimeMatches(runtime)) { UniquePtr task(Move(finished[i])); HelperThreadState().remove(finished, &i); task->complete(); @@ -5052,10 +5027,10 @@ SweepCompressionTasksTask::run() } } -/* virtual */ void -SweepWeakMapsTask::run() +static void +SweepWeakMaps(JSRuntime* runtime) { - for (GCSweepGroupIter zone(runtime()); !zone.done(); zone.next()) { + for (GCSweepGroupIter zone(runtime); !zone.done(); zone.next()) { /* Clear all weakrefs that point to unmarked things. */ for (auto edge : zone->gcWeakRefs()) { /* Edges may be present multiple times, so may already be nulled. */ @@ -5073,11 +5048,11 @@ SweepWeakMapsTask::run() } } -/* virtual */ void -SweepUniqueIdsTask::run() +static void +SweepUniqueIds(JSRuntime* runtime) { FreeOp fop(nullptr); - for (GCSweepGroupIter zone(runtime()); !zone.done(); zone.next()) + for (GCSweepGroupIter zone(runtime); !zone.done(); zone.next()) zone->sweepUniqueIds(&fop); } @@ -5207,33 +5182,44 @@ PrepareWeakCacheTasks(JSRuntime* rt) return tasks; } -template -class js::gc::AutoRunGCSweepTask +class MOZ_RAII js::gc::AutoRunParallelTask : public GCParallelTask { - Task task_; - GCRuntime* gc_; + using Func = void (*)(JSRuntime*); + + Func func_; + gcstats::Phase phase_; AutoLockHelperThreadState& lock_; public: - AutoRunGCSweepTask(GCRuntime* gc, AutoLockHelperThreadState& lock) - : task_(gc->rt), gc_(gc), lock_(lock) + AutoRunParallelTask(JSRuntime* rt, Func func, gcstats::Phase phase, + AutoLockHelperThreadState& lock) + : GCParallelTask(rt), + func_(func), + phase_(phase), + lock_(lock) { - gc_->startTask(task_, Task::StatsPhase, lock_); + runtime()->gc.startTask(*this, phase_, lock_); } - ~AutoRunGCSweepTask() { - gc_->joinTask(task_, Task::StatsPhase, lock_); + ~AutoRunParallelTask() { + runtime()->gc.joinTask(*this, phase_, lock_); + } + + void run() override { + func_(runtime()); } }; void -GCRuntime::beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock) +GCRuntime::beginSweepingSweepGroup() { /* * Begin sweeping the group of zones in currentSweepGroup, performing * actions that must be done before yielding to caller. */ + using namespace gcstats; + bool sweepingAtoms = false; for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { /* Set the GC state to sweeping. */ @@ -5254,17 +5240,16 @@ GCRuntime::beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock) validateIncrementalMarking(); FreeOp fop(rt); - WeakCacheTaskVector sweepCacheTasks = PrepareWeakCacheTasks(rt); { - gcstats::AutoPhase ap(stats(), gcstats::PHASE_FINALIZE_START); + AutoPhase ap(stats(), PHASE_FINALIZE_START); callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_PREPARE); { - gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_ZONES_CALLBACK); + AutoPhase ap2(stats(), PHASE_WEAK_ZONES_CALLBACK); callWeakPointerZonesCallbacks(); } { - gcstats::AutoPhase ap2(stats(), gcstats::PHASE_WEAK_COMPARTMENT_CALLBACK); + AutoPhase ap2(stats(), PHASE_WEAK_COMPARTMENT_CALLBACK); for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) callWeakPointerCompartmentCallbacks(comp); @@ -5276,39 +5261,41 @@ GCRuntime::beginSweepingSweepGroup(AutoLockForExclusiveAccess& lock) sweepDebuggerOnMainThread(&fop); { - AutoLockHelperThreadState helperLock; + AutoLockHelperThreadState lock; - Maybe> sweepAtoms; + Maybe sweepAtoms; if (sweepingAtoms) - sweepAtoms.emplace(this, helperLock); - - gcstats::AutoPhase ap(stats(), gcstats::PHASE_SWEEP_COMPARTMENTS); - gcstats::AutoSCC scc(stats(), sweepGroupIndex); - - AutoRunGCSweepTask sweepCCWrappers(this, helperLock); - AutoRunGCSweepTask sweepObjectGroups(this, helperLock); - AutoRunGCSweepTask sweepRegExps(this, helperLock); - AutoRunGCSweepTask sweepMisc(this, helperLock); - AutoRunGCSweepTask sweepCompressionTasks(this, helperLock); - AutoRunGCSweepTask sweepWeakMaps(this, helperLock); - AutoRunGCSweepTask sweepUniqueIds(this, helperLock); + sweepAtoms.emplace(rt, SweepAtoms, PHASE_SWEEP_ATOMS, lock); + + AutoPhase ap(stats(), PHASE_SWEEP_COMPARTMENTS); + AutoSCC scc(stats(), sweepGroupIndex); + + AutoRunParallelTask sweepCCWrappers(rt, SweepCCWrappers, PHASE_SWEEP_CC_WRAPPER, lock); + AutoRunParallelTask sweepObjectGroups(rt, SweepObjectGroups, PHASE_SWEEP_TYPE_OBJECT, lock); + AutoRunParallelTask sweepRegExps(rt, SweepRegExps, PHASE_SWEEP_REGEXP, lock); + AutoRunParallelTask sweepMisc(rt, SweepMisc, PHASE_SWEEP_MISC, lock); + AutoRunParallelTask sweepCompTasks(rt, SweepCompressionTasks, PHASE_SWEEP_COMPRESSION, lock); + AutoRunParallelTask sweepWeakMaps(rt, SweepWeakMaps, PHASE_SWEEP_WEAKMAPS, lock); + AutoRunParallelTask sweepUniqueIds(rt, SweepUniqueIds, PHASE_SWEEP_UNIQUEIDS, lock); + + WeakCacheTaskVector sweepCacheTasks = PrepareWeakCacheTasks(rt); for (auto& task : sweepCacheTasks) - startTask(task, gcstats::PHASE_SWEEP_WEAK_CACHES, helperLock); + startTask(task, PHASE_SWEEP_WEAK_CACHES, lock); { - AutoUnlockHelperThreadState unlock(helperLock); + AutoUnlockHelperThreadState unlock(lock); sweepJitDataOnMainThread(&fop); } for (auto& task : sweepCacheTasks) - joinTask(task, gcstats::PHASE_SWEEP_WEAK_CACHES, helperLock); + joinTask(task, PHASE_SWEEP_WEAK_CACHES, lock); } // Queue all GC things in all zones for sweeping, either on the foreground // or on the background thread. for (GCSweepGroupIter zone(rt); !zone.done(); zone.next()) { - gcstats::AutoSCC scc(stats(), sweepGroupIndex); + AutoSCC scc(stats(), sweepGroupIndex); zone->arenas.queueForForegroundSweep(&fop, ForegroundObjectFinalizePhase); for (unsigned i = 0; i < ArrayLength(IncrementalFinalizePhases); ++i) @@ -5390,7 +5377,7 @@ GCRuntime::beginSweepPhase(JS::gcreason::Reason reason, AutoLockForExclusiveAcce groupZonesForSweeping(reason, lock); endMarkingSweepGroup(); - beginSweepingSweepGroup(lock); + beginSweepingSweepGroup(); } bool @@ -5632,7 +5619,7 @@ GCRuntime::performSweepActions(SliceBudget& budget, AutoLockForExclusiveAccess& return Finished; endMarkingSweepGroup(); - beginSweepingSweepGroup(lock); + beginSweepingSweepGroup(); } } From e99ddb7ec222b126e27ea18d2f000d54bcd88643 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Thu, 4 May 2017 19:10:16 +0200 Subject: [PATCH 090/131] Bug 1360807 - FileReaderSync must work with sync inputStream - part 4 - Using BufferedInputStream for Base64EncodeInputStream, r=me --- dom/workers/FileReaderSync.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index c711232cf6d36..6a31df12ec1f7 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -263,19 +263,15 @@ FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, } // We need a buffered stream. - if (!NS_InputStreamIsBuffered(syncStream)) { - nsCOMPtr bufferedStream; - aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), - syncStream, 4096); - if (NS_WARN_IF(aRv.Failed())) { - return; - } - - syncStream = bufferedStream; + nsCOMPtr bufferedStream; + aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), + syncStream, 4096); + if (NS_WARN_IF(aRv.Failed())) { + return; } uint64_t size; - aRv = syncStream->Available(&size); + aRv = bufferedStream->Available(&size); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -291,7 +287,7 @@ FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult, } nsAutoString encodedData; - aRv = Base64EncodeInputStream(syncStream, encodedData, size); + aRv = Base64EncodeInputStream(bufferedStream, encodedData, size); if (NS_WARN_IF(aRv.Failed())){ return; } From db5049c0794740e5300362f0b8e109bc47161500 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Thu, 4 May 2017 13:21:17 -0400 Subject: [PATCH 091/131] Bug 1362063 - replace delayed ValidateARIAOwned on straightforward DOM tree traversal, r=davidb --- accessible/base/NotificationController.cpp | 4 - accessible/generic/DocAccessible.cpp | 98 +++++++++------------- accessible/generic/DocAccessible.h | 7 +- 3 files changed, 40 insertions(+), 69 deletions(-) diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index 4fe2072e7970e..529e29f98f3fb 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -795,10 +795,6 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) // modification are done. mDocument->ProcessInvalidationList(); - // We cannot rely on DOM tree to keep aria-owns relations updated. Make - // a validation to remove dead links. - mDocument->ValidateARIAOwned(); - // Process relocation list. for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) { if (mRelocations[idx]->IsInDocument()) { diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 4140fdbe72041..cd947b02d1e41 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -1972,19 +1972,30 @@ DocAccessible::FireEventsOnInsertion(Accessible* aContainer) } void -DocAccessible::ContentRemoved(Accessible* aContent) +DocAccessible::ContentRemoved(Accessible* aChild) { - MOZ_DIAGNOSTIC_ASSERT(aContent->Parent(), "Unattached accessible from tree"); + Accessible* parent = aChild->Parent(); + MOZ_DIAGNOSTIC_ASSERT(parent, "Unattached accessible from tree"); #ifdef A11Y_LOG logging::TreeInfo("process content removal", 0, - "container", aContent->Parent(), "child", aContent, nullptr); + "container", parent, "child", aChild, nullptr); #endif - TreeMutation mt(aContent->Parent()); - mt.BeforeRemoval(aContent); - aContent->Parent()->RemoveChild(aContent); - UncacheChildrenInSubtree(aContent); + TreeMutation mt(parent); + mt.BeforeRemoval(aChild); + + if (aChild->IsRelocated()) { + nsTArray >* owned = mARIAOwnsHash.Get(parent); + MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash"); + owned->RemoveElement(aChild); + if (owned->Length() == 0) { + mARIAOwnsHash.Remove(parent); + } + } + parent->RemoveChild(aChild); + UncacheChildrenInSubtree(aChild); + mt.Done(); } @@ -1996,11 +2007,11 @@ DocAccessible::ContentRemoved(nsIContent* aContentNode) if (acc) { ContentRemoved(acc); } - else { - TreeWalker walker(this, aContentNode); - while (Accessible* acc = walker.Next()) { - ContentRemoved(acc); - } + + dom::AllChildrenIterator iter = + dom::AllChildrenIterator(aContentNode, nsIContent::eAllChildren, true); + while (nsIContent* childNode = iter.GetNextChild()) { + ContentRemoved(childNode); } } @@ -2027,50 +2038,6 @@ DocAccessible::RelocateARIAOwnedIfNeeded(nsIContent* aElement) return false; } -void -DocAccessible::ValidateARIAOwned() -{ - for (auto it = mARIAOwnsHash.Iter(); !it.Done(); it.Next()) { - Accessible* owner = it.Key(); - nsTArray >* children = it.UserData(); - - // Owner is about to die, put children back if applicable. - if (owner != this && - (!mAccessibleCache.GetWeak(reinterpret_cast(owner)) || - !owner->IsInDocument())) { - PutChildrenBack(children, 0); - it.Remove(); - continue; - } - - for (uint32_t idx = 0; idx < children->Length(); idx++) { - Accessible* child = children->ElementAt(idx); - if (!child->IsInDocument()) { - children->RemoveElementAt(idx); - idx--; - continue; - } - - NS_ASSERTION(child->Parent(), "No parent for ARIA owned?"); - - // If DOM node doesn't have a frame anymore then shutdown its accessible. - if (child->Parent() && !child->GetFrame()) { - ContentRemoved(child); - children->RemoveElementAt(idx); - idx--; - continue; - } - - NS_ASSERTION(child->Parent() == owner, - "Illigally stolen ARIA owned child!"); - } - - if (children->Length() == 0) { - it.Remove(); - } - } -} - void DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner) { @@ -2194,7 +2161,7 @@ DocAccessible::PutChildrenBack(nsTArray >* aChildren, Accessible* prevChild = walker.Prev(); if (prevChild) { idxInParent = prevChild->IndexInParent() + 1; - MOZ_ASSERT(origContainer == prevChild->Parent(), "Broken tree"); + MOZ_DIAGNOSTIC_ASSERT(origContainer == prevChild->Parent(), "Broken tree"); origContainer = prevChild->Parent(); } else { @@ -2225,8 +2192,12 @@ DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent, // If the child was taken from from an ARIA owns element. if (aChild->IsRelocated()) { - nsTArray >* children = mARIAOwnsHash.Get(curParent); - children->RemoveElement(aChild); + nsTArray >* owned = mARIAOwnsHash.Get(curParent); + MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash"); + owned->RemoveElement(aChild); + if (owned->Length() == 0) { + mARIAOwnsHash.Remove(curParent); + } } NotificationController::MoveGuard mguard(mNotificationController); @@ -2327,10 +2298,19 @@ DocAccessible::UncacheChildrenInSubtree(Accessible* aRoot) aRoot->mStateFlags |= eIsNotInDocument; RemoveDependentIDsFor(aRoot); + nsTArray >* owned = mARIAOwnsHash.Get(aRoot); uint32_t count = aRoot->ContentChildCount(); for (uint32_t idx = 0; idx < count; idx++) { Accessible* child = aRoot->ContentChildAt(idx); + if (child->IsRelocated()) { + MOZ_ASSERT(owned, "IsRelocated flag is out of sync with mARIAOwnsHash"); + owned->RemoveElement(child); + if (owned->Length() == 0) { + mARIAOwnsHash.Remove(aRoot); + } + } + // Removing this accessible from the document doesn't mean anything about // accessibles for subdocuments, so skip removing those from the tree. if (!child->IsDoc()) { diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 92eeed1bc7983..2977fff4b1ed6 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -344,7 +344,7 @@ class DocAccessible : public HyperTextAccessibleWrap, /** * Update the tree on content removal. */ - void ContentRemoved(Accessible* aContent); + void ContentRemoved(Accessible* aAccessible); void ContentRemoved(nsIContent* aContentNode); /** @@ -504,11 +504,6 @@ class DocAccessible : public HyperTextAccessibleWrap, */ void ProcessInvalidationList(); - /** - * Validates all aria-owns connections and updates the tree accordingly. - */ - void ValidateARIAOwned(); - /** * Steals or puts back accessible subtrees. */ From d826543b276e11f84ffd2fe21395488fd79185b7 Mon Sep 17 00:00:00 2001 From: Michael Kaply Date: Thu, 4 May 2017 12:27:35 -0500 Subject: [PATCH 092/131] Bug 1320072 - Backout intent change - broke partner Google test. r=snorp --- .../org/mozilla/gecko/mozglue/SafeIntent.java | 14 ---- .../org/mozilla/search/SearchActivity.java | 64 +++---------------- .../SearchAndroidManifest_activities.xml.in | 9 --- 3 files changed, 9 insertions(+), 78 deletions(-) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java index 7ff2d77a4433f..6942962fe4f73 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/mozglue/SafeIntent.java @@ -10,7 +10,6 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.annotation.Nullable; import android.util.Log; import java.util.ArrayList; @@ -77,19 +76,6 @@ public String getStringExtra(final String name) { } } - @Nullable - public Bundle getExtras() { - try { - return intent.getExtras(); - } catch (OutOfMemoryError e) { - Log.w(LOGTAG, "Couldn't get intent extras: OOM. Malformed?"); - return null; - } catch (RuntimeException e) { - Log.w(LOGTAG, "Couldn't get intent extras.", e); - return null; - } - } - public Bundle getBundleExtra(final String name) { try { return intent.getBundleExtra(name); diff --git a/mobile/android/search/java/org/mozilla/search/SearchActivity.java b/mobile/android/search/java/org/mozilla/search/SearchActivity.java index 5f277ea9a8297..b013d77b4182b 100644 --- a/mobile/android/search/java/org/mozilla/search/SearchActivity.java +++ b/mobile/android/search/java/org/mozilla/search/SearchActivity.java @@ -4,7 +4,6 @@ package org.mozilla.search; -import android.app.SearchManager; import android.support.annotation.NonNull; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.Locales; @@ -13,7 +12,6 @@ import org.mozilla.gecko.TelemetryContract; import org.mozilla.gecko.db.BrowserContract.SearchHistory; import org.mozilla.gecko.distribution.Distribution; -import org.mozilla.gecko.mozglue.SafeIntent; import org.mozilla.gecko.search.SearchEngine; import org.mozilla.gecko.search.SearchEngineManager; import org.mozilla.gecko.search.SearchEngineManager.SearchEngineCallback; @@ -26,7 +24,6 @@ import android.content.Intent; import android.graphics.Rect; import android.os.Bundle; -import android.support.annotation.Nullable; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -50,8 +47,7 @@ public class SearchActivity extends Locales.LocaleAwareFragmentActivity private static final String KEY_SEARCH_STATE = "search_state"; private static final String KEY_EDIT_STATE = "edit_state"; - private static final String KEY_QUERY = SearchManager.QUERY; - private static final String KEY_INCOMING_QUERY = "incoming_query"; + private static final String KEY_QUERY = "query"; static enum SearchState { PRESEARCH, @@ -178,28 +174,16 @@ public void onClick(View v) { cardPaddingX = getResources().getDimensionPixelSize(R.dimen.search_row_padding); cardPaddingY = getResources().getDimensionPixelSize(R.dimen.search_row_padding); - final String query; - - final String incomingQuery = extractQuery(getIntent()); - final String previousInstanceQuery = extractQuery(savedInstanceState); - final String previousIncomingQuery = savedInstanceState == null ? null : savedInstanceState.getString(KEY_INCOMING_QUERY); - - if (savedInstanceState != null && (TextUtils.isEmpty(incomingQuery) || incomingQuery.equals(previousIncomingQuery))) { + if (savedInstanceState != null) { setSearchState(SearchState.valueOf(savedInstanceState.getString(KEY_SEARCH_STATE))); setEditState(EditState.valueOf(savedInstanceState.getString(KEY_EDIT_STATE))); - query = previousInstanceQuery; - } else { - query = incomingQuery; - if (!TextUtils.isEmpty(query)) { - setSearchState(SearchState.POSTSEARCH); - } - } - if (!TextUtils.isEmpty(query)) { + final String query = savedInstanceState.getString(KEY_QUERY); searchBar.setText(query); + // If we're in the postsearch state, we need to re-do the query. if (searchState == SearchState.POSTSEARCH) { - onSearch(query); + startSearch(query); } } else { // If there isn't a state to restore, the activity will start in the presearch state, @@ -208,28 +192,6 @@ public void onClick(View v) { } } - @Nullable - private String extractQuery(Intent intent) { - return extractQuery(intent == null ? null : new SafeIntent(intent).getExtras()); - } - - @Nullable - private String extractQuery(Bundle bundle) { - if (bundle == null) { - return null; - } - - String queryString = (String) bundle.getCharSequence("android.intent.extra.PROCESS_TEXT_READONLY");; - if (TextUtils.isEmpty(queryString)) { - queryString = (String) bundle.getCharSequence("android.intent.extra.PROCESS_TEXT"); - } - if (TextUtils.isEmpty(queryString)) { - queryString = bundle.getString(KEY_QUERY); - } - - return queryString; - } - @Override protected void onDestroy() { super.onDestroy(); @@ -263,24 +225,16 @@ public void onNewIntent(Intent intent) { // Reset the activity in the presearch state if it was launched from a new intent. setSearchState(SearchState.PRESEARCH); - final String queryString = extractQuery(intent); - if(!TextUtils.isEmpty(queryString)) { - setSearchState(SearchState.POSTSEARCH); - searchBar.setText(queryString); - onSearch(queryString); - } else { - // Enter editing mode and reset the query. We must reset the query after entering - // edit mode in order for the suggestions to update. - setEditState(EditState.EDITING); - searchBar.setText(""); - } + // Enter editing mode and reset the query. We must reset the query after entering + // edit mode in order for the suggestions to update. + setEditState(EditState.EDITING); + searchBar.setText(""); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(KEY_INCOMING_QUERY, extractQuery(getIntent())); outState.putString(KEY_SEARCH_STATE, searchState.toString()); outState.putString(KEY_EDIT_STATE, editState.toString()); outState.putString(KEY_QUERY, searchBar.getText()); diff --git a/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in b/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in index 532fe2f67b87e..21aee71c0f9d1 100644 --- a/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in +++ b/mobile/android/search/manifests/SearchAndroidManifest_activities.xml.in @@ -9,19 +9,10 @@ android:theme="@style/AppTheme"> - - - - - - - - - - +