author | Phil Ringnalda <philringnalda@gmail.com> |
Wed, 24 Dec 2014 15:38:41 -0800 | |
changeset 221301 | 2acb12da981391c0fdd9fe38b5089a0f20d5c43e |
parent 221239 | 5cc172f5e94c21971023589d699b88be85a241a2 (current diff) |
parent 221300 | af9239757205e91a20070c548da5b3679c393f73 (diff) |
child 221302 | 687012e14ec9fd24c81d48b1e2e076e96e23d1ce |
child 221328 | d6d081fd3b8148dfcf254a16e8e323b713e3f799 |
push id | 28015 |
push user | philringnalda@gmail.com |
push date | Wed, 24 Dec 2014 23:38:54 +0000 |
treeherder | mozilla-central@2acb12da9813 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 37.0a1 |
first release with | nightly linux32
2acb12da9813
/
37.0a1
/
20141225030205
/
files
nightly linux64
2acb12da9813
/
37.0a1
/
20141225030205
/
files
nightly mac
2acb12da9813
/
37.0a1
/
20141225030205
/
files
nightly win32
2acb12da9813
/
37.0a1
/
20141225030205
/
files
nightly win64
2acb12da9813
/
37.0a1
/
20141225030205
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
37.0a1
/
20141225030205
/
pushlog to previous
nightly linux64
37.0a1
/
20141225030205
/
pushlog to previous
nightly mac
37.0a1
/
20141225030205
/
pushlog to previous
nightly win32
37.0a1
/
20141225030205
/
pushlog to previous
nightly win64
37.0a1
/
20141225030205
/
pushlog to previous
|
js/src/tests/ecma_7/SIMD/coercions.js | file | annotate | diff | comparison | revisions | |
media/webrtc/trunk/webrtc/tools/e2e_quality/audio/perf | file | annotate | diff | comparison | revisions |
--- a/build/clang-plugin/tests/Makefile.in +++ b/build/clang-plugin/tests/Makefile.in @@ -3,12 +3,12 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. # Build without any warning flags, and with clang verify flag for a # syntax-only build (no codegen). OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify include $(topsrcdir)/config/rules.mk -target:: $(OBJS) +export:: $(OBJS) # We don't actually build anything. .PHONY: $(OBJS)
--- a/dom/base/nsHostObjectProtocolHandler.cpp +++ b/dom/base/nsHostObjectProtocolHandler.cpp @@ -637,18 +637,23 @@ NS_GetStreamForBlobURI(nsIURI* aURI, nsI return blobImpl->GetInternalStream(aStream); } nsresult NS_GetStreamForMediaStreamURI(nsIURI* aURI, mozilla::DOMMediaStream** aStream) { NS_ASSERTION(IsMediaStreamURI(aURI), "Only call this with mediastream URIs"); + nsISupports* dataObject = GetDataObject(aURI); + if (!dataObject) { + return NS_ERROR_DOM_BAD_URI; + } + *aStream = nullptr; - return CallQueryInterface(GetDataObject(aURI), aStream); + return CallQueryInterface(dataObject, aStream); } NS_IMETHODIMP nsFontTableProtocolHandler::NewURI(const nsACString& aSpec, const char *aCharset, nsIURI *aBaseURI, nsIURI **aResult) {
--- a/dom/browser-element/BrowserElementChildPreload.js +++ b/dom/browser-element/BrowserElementChildPreload.js @@ -1338,17 +1338,16 @@ BrowserElementChild.prototype = { else if (state & Ci.nsIWebProgressListener.STATE_IS_INSECURE) { stateDesc = 'insecure'; } else { debug("Unexpected securitychange state!"); stateDesc = '???'; } - // XXX Until bug 764496 is fixed, this will always return false. var isEV = !!(state & Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL); sendAsyncMsg('securitychange', { state: stateDesc, extendedValidation: isEV }); }, onStatusChange: function(webProgress, request, status, message) {}, onProgressChange: function(webProgress, request, curSelfProgress, maxSelfProgress, curTotalProgress, maxTotalProgress) {},
--- a/dom/media/fmp4/apple/AppleVDADecoder.cpp +++ b/dom/media/fmp4/apple/AppleVDADecoder.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <CoreFoundation/CFString.h> #include "AppleUtils.h" #include "AppleVDADecoder.h" #include "AppleVDALinker.h" #include "mp4_demuxer/DecoderData.h" +#include "mp4_demuxer/H264.h" #include "MP4Decoder.h" #include "MediaData.h" #include "MacIOSurfaceImage.h" #include "mozilla/ArrayUtils.h" #include "nsAutoPtr.h" #include "nsCocoaFeatures.h" #include "nsThreadUtils.h" #include "prlog.h" @@ -38,19 +39,38 @@ AppleVDADecoder::AppleVDADecoder(const m , mTaskQueue(aVideoTaskQueue) , mCallback(aCallback) , mImageContainer(aImageContainer) , mDecoder(nullptr) , mIs106(!nsCocoaFeatures::OnLionOrLater()) { MOZ_COUNT_CTOR(AppleVDADecoder); // TODO: Verify aConfig.mime_type. + + // Retrieve video dimensions from H264 SPS NAL. + mPictureWidth = mConfig.image_width; + mPictureHeight = mConfig.image_height; + mMaxRefFrames = 4; + mp4_demuxer::SPSData spsdata; + if (mp4_demuxer::H264::DecodeSPSFromExtraData(mConfig.extra_data, spsdata) && + spsdata.pic_width && spsdata.pic_height) { + mPictureWidth = spsdata.pic_width; + mPictureHeight = spsdata.pic_height; + // max_num_ref_frames determines the size of the sliding window + // we need to queue that many frames in order to guarantee proper + // pts frames ordering. Use a minimum of 4 to ensure proper playback of + // non compliant videos. + mMaxRefFrames = + (spsdata.max_num_ref_frames + 1) > mMaxRefFrames ? + spsdata.max_num_ref_frames + 1 : mMaxRefFrames; + } + LOG("Creating AppleVDADecoder for %dx%d h.264 video", - mConfig.image_width, - mConfig.image_height + mPictureWidth, + mPictureHeight ); } AppleVDADecoder::~AppleVDADecoder() { MOZ_COUNT_DTOR(AppleVDADecoder); } @@ -265,27 +285,19 @@ AppleVDADecoder::OutputFrame(CVPixelBuff mCallback->Error(); return NS_ERROR_FAILURE; } // Frames come out in DTS order but we need to output them // in composition order. mReorderQueue.Push(data); // Assume a frame with a PTS <= current DTS is ready. - while (mReorderQueue.Length() > 0) { + while (mReorderQueue.Length() > mMaxRefFrames) { nsRefPtr<VideoData> readyData = mReorderQueue.Pop(); - if (readyData->mTime <= aFrameRef->decode_timestamp) { - LOG("returning queued frame with pts %lld", readyData->mTime); mCallback->Output(readyData); - } else { - LOG("requeued frame with pts %lld > %lld", - readyData->mTime, aFrameRef->decode_timestamp); - mReorderQueue.Push(readyData); - break; - } } LOG("%llu decoded frames queued", static_cast<unsigned long long>(mReorderQueue.Length())); return NS_OK; } nsresult @@ -406,21 +418,21 @@ AppleVDADecoder::CreateDecoderSpecificat { const uint8_t* extradata = mConfig.extra_data->Elements(); int extrasize = mConfig.extra_data->Length(); OSType format = 'avc1'; AutoCFRelease<CFNumberRef> avc_width = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, - &mConfig.image_width); + &mPictureWidth); AutoCFRelease<CFNumberRef> avc_height = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, - &mConfig.image_height); + &mPictureHeight); AutoCFRelease<CFNumberRef> avc_format = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &format); AutoCFRelease<CFDataRef> avc_data = CFDataCreate(kCFAllocatorDefault, extradata, extrasize);
--- a/dom/media/fmp4/apple/AppleVDADecoder.h +++ b/dom/media/fmp4/apple/AppleVDADecoder.h @@ -85,16 +85,19 @@ public: void ClearReorderedFrames(); CFDictionaryRef CreateOutputConfiguration(); const mp4_demuxer::VideoDecoderConfig& mConfig; nsRefPtr<MediaTaskQueue> mTaskQueue; MediaDataDecoderCallback* mCallback; nsRefPtr<layers::ImageContainer> mImageContainer; ReorderQueue mReorderQueue; + uint32_t mPictureWidth; + uint32_t mPictureHeight; + uint32_t mMaxRefFrames; private: VDADecoder mDecoder; bool mIs106; // Method to pass a frame to VideoToolbox for decoding. nsresult SubmitFrame(mp4_demuxer::MP4Sample* aSample); // Method to set up the decompression session.
--- a/dom/media/fmp4/apple/AppleVTDecoder.cpp +++ b/dom/media/fmp4/apple/AppleVTDecoder.cpp @@ -6,16 +6,17 @@ #include <CoreFoundation/CFString.h> #include "AppleCMLinker.h" #include "AppleUtils.h" #include "AppleVTDecoder.h" #include "AppleVTLinker.h" #include "mp4_demuxer/DecoderData.h" +#include "mp4_demuxer/H264.h" #include "MediaData.h" #include "MacIOSurfaceImage.h" #include "mozilla/ArrayUtils.h" #include "nsAutoPtr.h" #include "nsThreadUtils.h" #include "prlog.h" #include "VideoUtils.h" @@ -270,18 +271,18 @@ AppleVTDecoder::InitializeSession() LOG("AVCDecoderConfig %ld bytes sha1 %s", mConfig.extra_data->Length(), avc_digest.get()); #endif // LOG_MEDIA_SHA1 AutoCFRelease<CFDictionaryRef> extensions = CreateDecoderExtensions(); rv = CMVideoFormatDescriptionCreate(kCFAllocatorDefault, kCMVideoCodecType_H264, - mConfig.image_width, - mConfig.image_height, + mPictureWidth, + mPictureHeight, extensions, &mFormat); if (rv != noErr) { NS_ERROR("Couldn't create format description!"); return NS_ERROR_FAILURE; } // Contruct video decoder selection spec.
--- a/ipc/glue/Shmem.h +++ b/ipc/glue/Shmem.h @@ -260,17 +260,17 @@ private: reinterpret_cast<char*>(aSegment->memory()) + aSegment->Size(); return reinterpret_cast<uint32_t*>(endOfSegment - sizeof(uint32_t)); } #else void AssertInvariants() const; #endif - SharedMemory* mSegment; + SharedMemory* MOZ_NON_OWNING_REF mSegment; void* mData; size_t mSize; id_t mId; }; } // namespace ipc } // namespace mozilla
--- a/ipc/ipdl/moz.build +++ b/ipc/ipdl/moz.build @@ -8,13 +8,11 @@ if CONFIG['MOZ_IPDL_TESTS']: DIRS += ['test'] FAIL_ON_WARNINGS = True include('/ipc/chromium/chromium-config.mozbuild') FINAL_LIBRARY = 'xul' -GENERATED_INCLUDES += ['/ipc/ipdl/_ipdlheaders'] - # We #include some things in the dom/plugins/ directory that rely on # toolkit libraries. CXXFLAGS += CONFIG['TK_CFLAGS']
--- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -8,16 +8,17 @@ * JS SIMD pseudo-module. * Specification matches polyfill: * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module. */ #include "builtin/SIMD.h" +#include "mozilla/IntegerTypeTraits.h" #include "jsapi.h" #include "jsfriendapi.h" #include "builtin/TypedObject.h" #include "js/Value.h" #include "jsobjinlines.h" @@ -142,22 +143,27 @@ static bool SignMask(JSContext *cx, unsi TypeDescr &descr = typedObj.typeDescr(); if (descr.kind() != type::Simd || descr.as<SimdTypeDescr>().type() != SimdType::type) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, SimdTypeDescr::class_.name, "signMask", InformalValueTypeName(args.thisv())); return false; } - Elem *data = reinterpret_cast<Elem *>(typedObj.typedMem()); - int32_t mx = data[0] < 0.0 ? 1 : 0; - int32_t my = data[1] < 0.0 ? 1 : 0; - int32_t mz = data[2] < 0.0 ? 1 : 0; - int32_t mw = data[3] < 0.0 ? 1 : 0; - int32_t result = mx | my << 1 | mz << 2 | mw << 3; + // Load the data as integer so that we treat the sign bit consistently, + // since -0.0 is not less than zero, but it still has the sign bit set. + typedef typename mozilla::SignedStdintTypeForSize<sizeof(Elem)>::Type Int; + static_assert(SimdType::lanes * sizeof(Int) <= jit::Simd128DataSize, + "signMask access should respect the bounds of the type"); + const Elem *elems = reinterpret_cast<const Elem *>(typedObj.typedMem()); + int32_t result = 0; + for (unsigned i = 0; i < SimdType::lanes; ++i) { + Int x = mozilla::BitwiseCast<Int>(elems[i]); + result |= (x < 0) << i; + } args.rval().setInt32(result); return true; } #define SIGN_MASK(type) \ static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \ return SignMask<type>(cx, argc, vp); \ } @@ -291,53 +297,46 @@ CreateSimdClass(JSContext *cx, Handle<Gl return typeDescr; } bool SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - const unsigned LANES = 4; Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>()); if (args.length() == 1) { // SIMD type used as a coercion if (!CheckVectorObject(args[0], descr->type())) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR); return false; } args.rval().setObject(args[0].toObject()); return true; } - if (args.length() < LANES) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, - args.callee().getClass()->name, "3", "s"); - return false; - } - Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0)); if (!result) return false; switch (descr->type()) { case SimdTypeDescr::TYPE_INT32: { int32_t *mem = reinterpret_cast<int32_t*>(result->typedMem()); for (unsigned i = 0; i < 4; i++) { - if (!ToInt32(cx, args[i], &mem[i])) + if (!ToInt32(cx, args.get(i), &mem[i])) return false; } break; } case SimdTypeDescr::TYPE_FLOAT32: { float *mem = reinterpret_cast<float*>(result->typedMem()); for (unsigned i = 0; i < 4; i++) { - if (!RoundFloat32(cx, args[i], &mem[i])) + if (!RoundFloat32(cx, args.get(i), &mem[i])) return false; } break; } } args.rval().setObject(*result); return true; }
--- a/js/src/jit-test/tests/asm.js/testBug1099216.js +++ b/js/src/jit-test/tests/asm.js/testBug1099216.js @@ -1,8 +1,13 @@ +if (typeof SIMD === 'undefined' || !isSimdAvailable()) { + print("won't run tests as simd extensions aren't activated yet"); + quit(0); +} + (function(global) { "use asm"; var frd = global.Math.fround; var fx4 = global.SIMD.float32x4; var fsp = fx4.splat; function s(){} function d(x){x=fx4(x);} function e() {
--- a/js/src/jit-test/tests/asm.js/testSIMD.js +++ b/js/src/jit-test/tests/asm.js/testSIMD.js @@ -183,17 +183,17 @@ assertAsmTypeFail('glob', USE_ASM + "fun assertAsmTypeFail('glob', USE_ASM + "function f() {var x=42.; return x.signMask;} return f"); assertAsmTypeFail('glob', USE_ASM + FROUND + "function f() {var x=f32(42.); return x.signMask;} return f"); assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f() { var x=i4(1,2,3,4); return x.signMask | 0 } return f'), this)(), 0b0000); assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f() { var x=i4(0,-1, ' + INT32_MAX + ',' + INT32_MIN + '); return x.signMask | 0 } return f'), this)(), 0b1010); assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(1), f32(-13.37), f32(42), f32(-Infinity)); return x.signMask | 0 } return f'), this)(), 0b1010); assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var Infinity = glob.Infinity; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(0), f32(-0.000001), f32(Infinity)); return x.signMask | 0 } return f'), this)(), 0b0101); -assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var NaN = glob.NaN; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(NaN), f32(3.), f32(4.)); return x.signMask | 0 } return f'), this)(), 0b0001); +assertEq(asmLink(asmCompile('glob', USE_ASM + F32 + FROUND + 'var NaN = glob.NaN; function f() { var x=f4(0,0,0,0); x=f4(f32(-1), f32(NaN), f32(-0), f32(0)); return x.signMask | 0 } return f'), this)(), 0b0101); // 1.3.3. Variable assignments assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4();} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2, 3);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1.0, 2, 3, 4);} return f"); assertAsmTypeFail('glob', USE_ASM + I32 + I32A + "function f() {var x=i4(1,2,3,4); x=i4(1, 2.0, 3, 4);} return f");
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -1787,16 +1787,21 @@ CodeGenerator::visitLabel(LLabel *lir) } void CodeGenerator::visitNop(LNop *lir) { } void +CodeGenerator::visitMop(LMop *lir) +{ +} + +void CodeGenerator::visitOsiPoint(LOsiPoint *lir) { // Note: markOsiPoint ensures enough space exists between the last // LOsiPoint and this one to patch adjacent call instructions. MOZ_ASSERT(masm.framePushed() == frameSize()); uint32_t osiCallPointOffset = markOsiPoint(lir);
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -58,16 +58,17 @@ class CodeGenerator : public CodeGenerat public: bool generate(); bool generateAsmJS(AsmJSFunctionLabels *labels); bool link(JSContext *cx, types::CompilerConstraintList *constraints); void visitLabel(LLabel *lir); void visitNop(LNop *lir); + void visitMop(LMop *lir); void visitOsiPoint(LOsiPoint *lir); void visitGoto(LGoto *lir); void visitTableSwitch(LTableSwitch *ins); void visitTableSwitchV(LTableSwitchV *ins); void visitCloneLiteral(LCloneLiteral *lir); void visitParameter(LParameter *lir); void visitCallee(LCallee *lir); void visitIsConstructing(LIsConstructing *lir);
--- a/js/src/jit/EffectiveAddressAnalysis.cpp +++ b/js/src/jit/EffectiveAddressAnalysis.cpp @@ -16,20 +16,20 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l { if (lsh->specialization() != MIRType_Int32) return; MDefinition *index = lsh->lhs(); MOZ_ASSERT(index->type() == MIRType_Int32); MDefinition *shift = lsh->rhs(); - if (!shift->isConstant()) + if (!shift->isConstantValue()) return; - Value shiftValue = shift->toConstant()->value(); + Value shiftValue = shift->constantValue(); if (!shiftValue.isInt32() || !IsShiftInScaleRange(shiftValue.toInt32())) return; Scale scale = ShiftToScale(shiftValue.toInt32()); int32_t displacement = 0; MInstruction *last = lsh; MDefinition *base = nullptr; @@ -42,18 +42,18 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l break; MAdd *add = use->consumer()->toDefinition()->toAdd(); if (add->specialization() != MIRType_Int32 || !add->isTruncated()) break; MDefinition *other = add->getOperand(1 - add->indexOf(*use)); - if (other->isConstant()) { - displacement += other->toConstant()->value().toInt32(); + if (other->isConstantValue()) { + displacement += other->constantValue().toInt32(); } else { if (base) break; base = other; } last = add; } @@ -67,21 +67,21 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *l return; MUseIterator use = last->usesBegin(); if (!use->consumer()->isDefinition() || !use->consumer()->toDefinition()->isBitAnd()) return; MBitAnd *bitAnd = use->consumer()->toDefinition()->toBitAnd(); MDefinition *other = bitAnd->getOperand(1 - bitAnd->indexOf(*use)); - if (!other->isConstant() || !other->toConstant()->value().isInt32()) + if (!other->isConstantValue() || !other->constantValue().isInt32()) return; uint32_t bitsClearedByShift = elemSize - 1; - uint32_t bitsClearedByMask = ~uint32_t(other->toConstant()->value().toInt32()); + uint32_t bitsClearedByMask = ~uint32_t(other->constantValue().toInt32()); if ((bitsClearedByShift & bitsClearedByMask) != bitsClearedByMask) return; bitAnd->replaceAllUsesWith(last); return; } MEffectiveAddress *eaddr = MEffectiveAddress::New(alloc, base, index, scale, displacement);
--- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -255,29 +255,29 @@ MaybeFoldConditionBlock(MIRGraph &graph, // If either trueBranch or falseBranch just computes a constant for the // test, determine the block that branch will end up jumping to and eliminate // the branch. Otherwise, change the end of the block to a test that jumps // directly to successors of testBlock, rather than to testBlock itself. MBasicBlock *trueTarget = trueBranch; if (BlockComputesConstant(trueBranch, trueResult)) { - trueTarget = trueResult->toConstant()->valueToBoolean() + trueTarget = trueResult->constantToBoolean() ? finalTest->ifTrue() : finalTest->ifFalse(); testBlock->removePredecessor(trueBranch); graph.removeBlock(trueBranch); } else { UpdateTestSuccessors(graph.alloc(), trueBranch, trueResult, finalTest->ifTrue(), finalTest->ifFalse(), testBlock); } MBasicBlock *falseTarget = falseBranch; if (BlockComputesConstant(falseBranch, falseResult)) { - falseTarget = falseResult->toConstant()->valueToBoolean() + falseTarget = falseResult->constantToBoolean() ? finalTest->ifTrue() : finalTest->ifFalse(); testBlock->removePredecessor(falseBranch); graph.removeBlock(falseBranch); } else { UpdateTestSuccessors(graph.alloc(), falseBranch, falseResult, finalTest->ifTrue(), finalTest->ifFalse(), testBlock); } @@ -2265,18 +2265,18 @@ SimpleLinearSum jit::ExtractLinearSum(MDefinition *ins) { if (ins->isBeta()) ins = ins->getOperand(0); if (ins->type() != MIRType_Int32) return SimpleLinearSum(ins, 0); - if (ins->isConstant()) { - const Value &v = ins->toConstant()->value(); + if (ins->isConstantValue()) { + const Value &v = ins->constantValue(); MOZ_ASSERT(v.isInt32()); return SimpleLinearSum(nullptr, v.toInt32()); } else if (ins->isAdd() || ins->isSub()) { MDefinition *lhs = ins->getOperand(0); MDefinition *rhs = ins->getOperand(1); if (lhs->type() == MIRType_Int32 && rhs->type() == MIRType_Int32) { SimpleLinearSum lsum = ExtractLinearSum(lhs); SimpleLinearSum rsum = ExtractLinearSum(rhs); @@ -2667,18 +2667,18 @@ LinearSum::add(SimpleLinearSum other, in bool LinearSum::add(MDefinition *term, int32_t scale) { MOZ_ASSERT(term); if (scale == 0) return true; - if (term->isConstant()) { - int32_t constant = term->toConstant()->value().toInt32(); + if (term->isConstantValue()) { + int32_t constant = term->constantValue().toInt32(); if (!SafeMul(constant, scale, &constant)) return false; return add(constant); } for (size_t i = 0; i < terms_.length(); i++) { if (term == terms_[i].term) { if (!SafeAdd(scale, terms_[i].scale, &terms_[i].scale)) @@ -2746,17 +2746,17 @@ LinearSum::dump() const MDefinition * jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum &sum, bool convertConstant) { MDefinition *def = nullptr; for (size_t i = 0; i < sum.numTerms(); i++) { LinearTerm term = sum.term(i); - MOZ_ASSERT(!term.term->isConstant()); + MOZ_ASSERT(!term.term->isConstantValue()); if (term.scale == 1) { if (def) { def = MAdd::New(alloc, def, term.term); def->toAdd()->setInt32(); block->insertAtEnd(def->toInstruction()); def->computeRange(alloc); } else { def = term.term;
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -2256,19 +2256,18 @@ IonBuilder::processDoWhileCondEnd(CFGSta // Pop the last value, and create the successor block. MDefinition *vins = current->pop(); MBasicBlock *successor = newBlock(current, GetNextPc(pc), loopDepth_ - 1); if (!successor) return ControlStatus_Error; // Test for do {} while(false) and don't create a loop in that case. - if (vins->isConstant()) { - MConstant *cte = vins->toConstant(); - if (cte->value().isBoolean() && !cte->value().toBoolean()) { + if (vins->isConstantValue() && !vins->constantValue().isMagic()) { + if (!vins->constantToBoolean()) { current->end(MGoto::New(alloc(), successor)); current = nullptr; state.loop.successor = successor; return processBrokenLoop(state); } } @@ -4563,17 +4562,17 @@ IonBuilder::makeInliningDecision(JSObjec // // - Inner function as argument. Inlining a function called // with an inner function might help scalar replacement at // removing the scope chain, and thus using registers within // the loop instead of writting everything back to memory. bool hasOpportunities = false; for (size_t i = 0, e = callInfo.argv().length(); !hasOpportunities && i < e; i++) { MDefinition *arg = callInfo.argv()[i]; - hasOpportunities = arg->isLambda() || arg->isConstant(); + hasOpportunities = arg->isLambda() || arg->isConstantValue(); } if (!hasOpportunities) return DontInline(targetScript, "Vetoed: big function that contains a loop"); } // Caller must not be excessively large. if (script()->length() >= optimizationInfo().inliningMaxCallerBytecodeLength()) @@ -5925,20 +5924,20 @@ IonBuilder::jsop_eval(uint32_t argc) current->pushSlot(info().thisSlot()); MDefinition *thisValue = current->pop(); // Try to pattern match 'eval(v + "()")'. In this case v is likely a // name on the scope chain and the eval is performing a call on that // value. Use a dynamic scope chain lookup rather than a full eval. if (string->isConcat() && - string->getOperand(1)->isConstant() && - string->getOperand(1)->toConstant()->value().isString()) + string->getOperand(1)->isConstantValue() && + string->getOperand(1)->constantValue().isString()) { - JSAtom *atom = &string->getOperand(1)->toConstant()->value().toString()->asAtom(); + JSAtom *atom = &string->getOperand(1)->constantValue().toString()->asAtom(); if (StringEqualsAscii(atom, "()")) { MDefinition *name = string->getOperand(0); MInstruction *dynamicName = MGetDynamicName::New(alloc(), scopeChain, name); current->add(dynamicName); current->push(dynamicName); current->push(thisValue); @@ -7841,20 +7840,20 @@ IonBuilder::getElemTryArgumentsInlined(b return true; // Emit inlined arguments. obj->setImplicitlyUsedUnchecked(); MOZ_ASSERT(!info().argsObjAliasesFormals()); // When the id is constant, we can just return the corresponding inlined argument - if (index->isConstant() && index->toConstant()->value().isInt32()) { + if (index->isConstantValue() && index->constantValue().isInt32()) { MOZ_ASSERT(inliningDepth_ > 0); - int32_t id = index->toConstant()->value().toInt32(); + int32_t id = index->constantValue().toInt32(); index->setImplicitlyUsedUnchecked(); if (id < (int32_t)inlineCallInfo_->argc() && id >= 0) current->push(inlineCallInfo_->getArg(id)); else pushConstant(UndefinedValue()); *emitted = true; @@ -8060,18 +8059,18 @@ void IonBuilder::addTypedArrayLengthAndData(MDefinition *obj, BoundsChecking checking, MDefinition **index, MInstruction **length, MInstruction **elements) { MOZ_ASSERT((index != nullptr) == (elements != nullptr)); JSObject *tarr = nullptr; - if (obj->isConstant() && obj->toConstant()->value().isObject()) - tarr = &obj->toConstant()->value().toObject(); + if (obj->isConstantValue() && obj->constantValue().isObject()) + tarr = &obj->constantValue().toObject(); else if (obj->resultTypeSet()) tarr = obj->resultTypeSet()->getSingleton(); if (tarr) { void *data = AnyTypedArrayViewData(tarr); // Bug 979449 - Optimistically embed the elements and use TI to // invalidate if we move them. bool isTenured = !tarr->runtimeFromMainThread()->gc.nursery.isInside(data); @@ -8118,28 +8117,28 @@ IonBuilder::convertShiftToMaskForStaticT Scalar::Type viewType) { // No shifting is necessary if the typed array has single byte elements. if (TypedArrayShift(viewType) == 0) return id; // If the index is an already shifted constant, undo the shift to get the // absolute offset being accessed. - if (id->isConstant() && id->toConstant()->value().isInt32()) { - int32_t index = id->toConstant()->value().toInt32(); + if (id->isConstantValue() && id->constantValue().isInt32()) { + int32_t index = id->constantValue().toInt32(); MConstant *offset = MConstant::New(alloc(), Int32Value(index << TypedArrayShift(viewType))); current->add(offset); return offset; } if (!id->isRsh() || id->isEffectful()) return nullptr; - if (!id->getOperand(1)->isConstant()) + if (!id->getOperand(1)->isConstantValue()) return nullptr; - const Value &value = id->getOperand(1)->toConstant()->value(); + const Value &value = id->getOperand(1)->constantValue(); if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType)) return nullptr; // Instead of shifting, mask off the low bits of the index so that // a non-scaled access on the typed array can be performed. MConstant *mask = MConstant::New(alloc(), Int32Value(~((1 << value.toInt32()) - 1))); MBitAnd *ptr = MBitAnd::New(alloc(), id->getOperand(0), mask);
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -38,16 +38,22 @@ class LLabel : public LInstructionHelper }; class LNop : public LInstructionHelper<0, 0, 0> { public: LIR_HEADER(Nop) }; +class LMop : public LInstructionHelper<0, 0, 0> +{ + public: + LIR_HEADER(Mop) +}; + // An LOsiPoint captures a snapshot after a call and ensures enough space to // patch in a call to the invalidation mechanism. // // Note: LSafepoints are 1:1 with LOsiPoints, so it holds a reference to the // corresponding LSafepoint to inform it of the LOsiPoint's masm offset when it // gets CG'd. class LOsiPoint : public LInstructionHelper<0, 0, 0> {
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jit_LOpcodes_h #define jit_LOpcodes_h #define LIR_COMMON_OPCODE_LIST(_) \ _(Label) \ _(Nop) \ + _(Mop) \ _(OsiPoint) \ _(MoveGroup) \ _(Integer) \ _(Pointer) \ _(Double) \ _(Float32) \ _(SimdSplatX4) \ _(Int32x4) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -618,17 +618,17 @@ LIRGenerator::visitCallDirectEval(MCallD } static JSOp ReorderComparison(JSOp op, MDefinition **lhsp, MDefinition **rhsp) { MDefinition *lhs = *lhsp; MDefinition *rhs = *rhsp; - if (lhs->isConstant()) { + if (lhs->isConstantValue()) { *rhsp = lhs; *lhsp = rhs; return ReverseCompareOp(op); } return op; } void @@ -638,18 +638,18 @@ LIRGenerator::visitTest(MTest *test) MBasicBlock *ifTrue = test->ifTrue(); MBasicBlock *ifFalse = test->ifFalse(); // String is converted to length of string in the type analysis phase (see // TestPolicy). MOZ_ASSERT(opd->type() != MIRType_String); // Testing a constant. - if (opd->isConstant()) { - bool result = opd->toConstant()->valueToBoolean(); + if (opd->isConstantValue() && !opd->constantValue().isMagic()) { + bool result = opd->constantToBoolean(); add(new(alloc()) LGoto(result ? ifTrue : ifFalse)); return; } if (opd->type() == MIRType_Value) { LDefinition temp0, temp1; if (test->operandMightEmulateUndefined()) { temp0 = temp(); @@ -1486,35 +1486,35 @@ LIRGenerator::visitMul(MMul *ins) MOZ_ASSERT(lhs->type() == rhs->type()); if (ins->specialization() == MIRType_Int32) { MOZ_ASSERT(lhs->type() == MIRType_Int32); ReorderCommutative(&lhs, &rhs, ins); // If our RHS is a constant -1 and we don't have to worry about // overflow, we can optimize to an LNegI. - if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->value() == Int32Value(-1)) + if (!ins->fallible() && rhs->isConstantValue() && rhs->constantValue() == Int32Value(-1)) defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0); else lowerMulI(ins, lhs, rhs); } else if (ins->specialization() == MIRType_Double) { MOZ_ASSERT(lhs->type() == MIRType_Double); ReorderCommutative(&lhs, &rhs, ins); // If our RHS is a constant -1.0, we can optimize to an LNegD. - if (rhs->isConstant() && rhs->toConstant()->value() == DoubleValue(-1.0)) + if (rhs->isConstantValue() && rhs->constantValue() == DoubleValue(-1.0)) defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0); else lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs); } else if (ins->specialization() == MIRType_Float32) { MOZ_ASSERT(lhs->type() == MIRType_Float32); ReorderCommutative(&lhs, &rhs, ins); // We apply the same optimizations as for doubles - if (rhs->isConstant() && rhs->toConstant()->value() == Float32Value(-1.0f)) + if (rhs->isConstantValue() && rhs->constantValue() == Float32Value(-1.0f)) defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0); else lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs); } else { lowerBinaryV(JSOP_MUL, ins); } } @@ -4112,20 +4112,30 @@ LIRGenerator::visitInstruction(MInstruct if (ins->resumePoint()) updateResumeState(ins); #ifdef DEBUG ins->setInWorklistUnchecked(); #endif + // If we added a Nop for this instruction, we'll also add a Mop, so that + // that live-ranges for fixed register defs, which with LSRA extend through + // the Nop so that they can extend through the OsiPoint don't, with their + // one-extra extension, extend into a position where they use the input + // move group for the following instruction. + bool needsMop = !current->instructions().empty() && current->rbegin()->isNop(); + // If no safepoint was created, there's no need for an OSI point. if (LOsiPoint *osiPoint = popOsiPoint()) add(osiPoint); + if (needsMop) + add(new(alloc()) LMop); + return !gen->errored(); } void LIRGenerator::definePhis() { size_t lirIndex = 0; MBasicBlock *block = current->mir();
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -371,30 +371,30 @@ IonBuilder::inlineArray(CallInfo &callIn templateObject->clearShouldConvertDoubleElements(); // A single integer argument denotes initial length. if (callInfo.argc() == 1) { if (callInfo.getArg(0)->type() != MIRType_Int32) return InliningStatus_NotInlined; MDefinition *arg = callInfo.getArg(0); - if (!arg->isConstant()) { + if (!arg->isConstantValue()) { callInfo.setImplicitlyUsedUnchecked(); ArrayObject *templateArray = &templateObject->as<ArrayObject>(); MNewArrayDynamicLength *ins = MNewArrayDynamicLength::New(alloc(), constraints(), templateArray, templateArray->type()->initialHeap(constraints()), arg); current->add(ins); current->push(ins); return InliningStatus_Inlined; } // Negative lengths generate a RangeError, unhandled by the inline path. - initLength = arg->toConstant()->value().toInt32(); + initLength = arg->constantValue().toInt32(); if (initLength >= NativeObject::NELEMENTS_LIMIT) return InliningStatus_NotInlined; // Make sure initLength matches the template object's length. This is // not guaranteed to be the case, for instance if we're inlining the // MConstant may come from an outer script. if (initLength != templateObject->as<ArrayObject>().length()) return InliningStatus_NotInlined; @@ -1031,20 +1031,20 @@ IonBuilder::inlineMathPow(CallInfo &call callInfo.setImplicitlyUsedUnchecked(); MDefinition *base = callInfo.getArg(0); MDefinition *power = callInfo.getArg(1); MDefinition *output = nullptr; // Optimize some constant powers. - if (callInfo.getArg(1)->isConstant() && - callInfo.getArg(1)->toConstant()->value().isNumber()) + if (callInfo.getArg(1)->isConstantValue() && + callInfo.getArg(1)->constantValue().isNumber()) { - double pow = callInfo.getArg(1)->toConstant()->value().toNumber(); + double pow = callInfo.getArg(1)->constantValue().toNumber(); // Math.pow(x, 0.5) is a sqrt with edge-case detection. if (pow == 0.5) { MPowHalf *half = MPowHalf::New(alloc(), base); current->add(half); output = half; } @@ -1209,18 +1209,18 @@ IonBuilder::inlineMathMinMax(CallInfo &c case MIRType_Int32: if (!int32_cases.append(arg)) return InliningStatus_Error; break; case MIRType_Double: case MIRType_Float32: // Don't force a double MMinMax for arguments that would be a NOP // when doing an integer MMinMax. - if (arg->isConstant()) { - double cte = arg->toConstant()->value().toDouble(); + if (arg->isConstantValue()) { + double cte = arg->constantValue().toDouble(); // min(int32, cte >= INT32_MAX) = int32 if (cte >= INT32_MAX && !max) break; // max(int32, cte <= INT32_MIN) = int32 if (cte <= INT32_MIN && max) break; } @@ -1361,24 +1361,24 @@ IonBuilder::inlineStrCharCodeAt(CallInfo current->add(charCode); current->push(charCode); return InliningStatus_Inlined; } IonBuilder::InliningStatus IonBuilder::inlineConstantCharCodeAt(CallInfo &callInfo) { - if (!callInfo.thisArg()->isConstant()) + if (!callInfo.thisArg()->isConstantValue()) return InliningStatus_NotInlined; - if (!callInfo.getArg(0)->isConstant()) + if (!callInfo.getArg(0)->isConstantValue()) return InliningStatus_NotInlined; - const js::Value *strval = callInfo.thisArg()->toConstant()->vp(); - const js::Value *idxval = callInfo.getArg(0)->toConstant()->vp(); + const js::Value *strval = callInfo.thisArg()->constantVp(); + const js::Value *idxval = callInfo.getArg(0)->constantVp(); if (!strval->isString() || !idxval->isInt32()) return InliningStatus_NotInlined; JSString *str = strval->toString(); if (!str->isLinear()) return InliningStatus_NotInlined; @@ -2026,19 +2026,19 @@ IonBuilder::inlineUnsafeSetReservedSlot( return InliningStatus_NotInlined; if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; if (callInfo.getArg(1)->type() != MIRType_Int32) return InliningStatus_NotInlined; // Don't inline if we don't have a constant slot. MDefinition *arg = callInfo.getArg(1); - if (!arg->isConstant()) + if (!arg->isConstantValue()) return InliningStatus_NotInlined; - uint32_t slot = arg->toConstant()->value().toPrivateUint32(); + uint32_t slot = arg->constantValue().toPrivateUint32(); callInfo.setImplicitlyUsedUnchecked(); MStoreFixedSlot *store = MStoreFixedSlot::New(alloc(), callInfo.getArg(0), slot, callInfo.getArg(2)); current->add(store); current->push(store); if (NeedsPostBarrier(info(), callInfo.getArg(2))) @@ -2054,19 +2054,19 @@ IonBuilder::inlineUnsafeGetReservedSlot( return InliningStatus_NotInlined; if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined; if (callInfo.getArg(1)->type() != MIRType_Int32) return InliningStatus_NotInlined; // Don't inline if we don't have a constant slot. MDefinition *arg = callInfo.getArg(1); - if (!arg->isConstant()) + if (!arg->isConstantValue()) return InliningStatus_NotInlined; - uint32_t slot = arg->toConstant()->value().toPrivateUint32(); + uint32_t slot = arg->constantValue().toPrivateUint32(); callInfo.setImplicitlyUsedUnchecked(); MLoadFixedSlot *load = MLoadFixedSlot::New(alloc(), callInfo.getArg(0), slot); current->add(load); current->push(load); // We don't track reserved slot types, so always emit a barrier. @@ -2222,19 +2222,19 @@ IonBuilder::inlineBailout(CallInfo &call IonBuilder::InliningStatus IonBuilder::inlineAssertFloat32(CallInfo &callInfo) { callInfo.setImplicitlyUsedUnchecked(); MDefinition *secondArg = callInfo.getArg(1); MOZ_ASSERT(secondArg->type() == MIRType_Boolean); - MOZ_ASSERT(secondArg->isConstant()); - - bool mustBeFloat32 = secondArg->toConstant()->value().toBoolean(); + MOZ_ASSERT(secondArg->isConstantValue()); + + bool mustBeFloat32 = secondArg->constantValue().toBoolean(); current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32)); MConstant *undefined = MConstant::New(alloc(), UndefinedValue()); current->add(undefined); current->push(undefined); return InliningStatus_Inlined; }
--- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -66,27 +66,55 @@ MDefinition::PrintOpcodeName(FILE *fp, M #undef NAME }; const char *name = names[op]; size_t len = strlen(name); for (size_t i = 0; i < len; i++) fprintf(fp, "%c", tolower(name[i])); } +const Value & +MDefinition::constantValue() +{ + MOZ_ASSERT(isConstantValue()); + + if (isBox()) + return getOperand(0)->constantValue(); + return toConstant()->value(); +} + +const Value * +MDefinition::constantVp() +{ + MOZ_ASSERT(isConstantValue()); + if (isBox()) + return getOperand(0)->constantVp(); + return toConstant()->vp(); +} + +bool +MDefinition::constantToBoolean() +{ + MOZ_ASSERT(isConstantValue()); + if (isBox()) + return getOperand(0)->constantToBoolean(); + return toConstant()->valueToBoolean(); +} + static MConstant * EvaluateConstantOperands(TempAllocator &alloc, MBinaryInstruction *ins, bool *ptypeChange = nullptr) { MDefinition *left = ins->getOperand(0); MDefinition *right = ins->getOperand(1); - if (!left->isConstant() || !right->isConstant()) + if (!left->isConstantValue() || !right->isConstantValue()) return nullptr; - Value lhs = left->toConstant()->value(); - Value rhs = right->toConstant()->value(); + Value lhs = left->constantValue(); + Value rhs = right->constantValue(); Value ret = UndefinedValue(); switch (ins->op()) { case MDefinition::Op_BitAnd: ret = Int32Value(lhs.toInt32() & rhs.toInt32()); break; case MDefinition::Op_BitOr: ret = Int32Value(lhs.toInt32() | rhs.toInt32()); @@ -141,20 +169,20 @@ EvaluateExactReciprocal(TempAllocator &a { // we should fold only when it is a floating point operation if (!IsFloatingPointType(ins->type())) return nullptr; MDefinition *left = ins->getOperand(0); MDefinition *right = ins->getOperand(1); - if (!right->isConstant()) + if (!right->isConstantValue()) return nullptr; - Value rhs = right->toConstant()->value(); + Value rhs = right->constantValue(); int32_t num; if (!mozilla::NumberIsInt32(rhs.toNumber(), &num)) return nullptr; // check if rhs is a power of two if (mozilla::Abs(num) & (mozilla::Abs(num) - 1)) return nullptr; @@ -350,18 +378,18 @@ MTest::foldsTo(TempAllocator &alloc) if (op->isNot()) { // If the operand of the Not is itself a Not, they cancel out. MDefinition *opop = op->getOperand(0); if (opop->isNot()) return MTest::New(alloc, opop->toNot()->input(), ifTrue(), ifFalse()); return MTest::New(alloc, op->toNot()->input(), ifFalse(), ifTrue()); } - if (op->isConstant()) - return MGoto::New(alloc, op->toConstant()->valueToBoolean() ? ifTrue() : ifFalse()); + if (op->isConstantValue() && !op->constantValue().isMagic()) + return MGoto::New(alloc, op->constantToBoolean() ? ifTrue() : ifFalse()); switch (op->type()) { case MIRType_Undefined: case MIRType_Null: return MGoto::New(alloc, ifFalse()); case MIRType_Symbol: return MGoto::New(alloc, ifTrue()); case MIRType_Object: @@ -763,39 +791,39 @@ MSimdValueX4::foldsTo(TempAllocator &all { DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type()); bool allConstants = true; bool allSame = true; for (size_t i = 0; i < 4; ++i) { MDefinition *op = getOperand(i); MOZ_ASSERT(op->type() == scalarType); - if (!op->isConstant()) + if (!op->isConstantValue()) allConstants = false; if (i > 0 && op != getOperand(i - 1)) allSame = false; } if (!allConstants && !allSame) return this; if (allConstants) { SimdConstant cst; switch (type()) { case MIRType_Int32x4: { int32_t a[4]; for (size_t i = 0; i < 4; ++i) - a[i] = getOperand(i)->toConstant()->value().toInt32(); + a[i] = getOperand(i)->constantValue().toInt32(); cst = SimdConstant::CreateX4(a); break; } case MIRType_Float32x4: { float a[4]; for (size_t i = 0; i < 4; ++i) - a[i] = getOperand(i)->toConstant()->value().toNumber(); + a[i] = getOperand(i)->constantValue().toNumber(); cst = SimdConstant::CreateX4(a); break; } default: MOZ_CRASH("unexpected type in MSimdValueX4::foldsTo"); } return MSimdConstant::New(alloc, cst, type()); } @@ -804,33 +832,33 @@ MSimdValueX4::foldsTo(TempAllocator &all return MSimdSplatX4::New(alloc, type(), getOperand(0)); } MDefinition* MSimdSplatX4::foldsTo(TempAllocator &alloc) { DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type()); MDefinition *op = getOperand(0); - if (!op->isConstant()) + if (!op->isConstantValue()) return this; MOZ_ASSERT(op->type() == scalarType); SimdConstant cst; switch (type()) { case MIRType_Int32x4: { int32_t a[4]; - int32_t v = getOperand(0)->toConstant()->value().toInt32(); + int32_t v = getOperand(0)->constantValue().toInt32(); for (size_t i = 0; i < 4; ++i) a[i] = v; cst = SimdConstant::CreateX4(a); break; } case MIRType_Float32x4: { float a[4]; - float v = getOperand(0)->toConstant()->value().toNumber(); + float v = getOperand(0)->constantValue().toNumber(); for (size_t i = 0; i < 4; ++i) a[i] = v; cst = SimdConstant::CreateX4(a); break; } default: MOZ_CRASH("unexpected type in MSimdSplatX4::foldsTo"); } @@ -1091,18 +1119,18 @@ MApplyArgs::New(TempAllocator &alloc, JS MDefinition *self) { return new(alloc) MApplyArgs(target, fun, argc, self); } MDefinition* MStringLength::foldsTo(TempAllocator &alloc) { - if ((type() == MIRType_Int32) && (string()->isConstant())) { - Value value = string()->toConstant()->value(); + if ((type() == MIRType_Int32) && (string()->isConstantValue())) { + Value value = string()->constantValue(); JSAtom *atom = &value.toString()->asAtom(); return MConstant::New(alloc, Int32Value(atom->length())); } return this; } static bool @@ -1879,22 +1907,20 @@ MMinMax::trySpecializeFloat32(TempAlloca } MDefinition * MMinMax::foldsTo(TempAllocator &alloc) { if (!lhs()->isConstant() && !rhs()->isConstant()) return this; - MDefinition *operand = lhs()->isConstant() ? rhs() : lhs(); - MConstant *constant = lhs()->isConstant() ? lhs()->toConstant() : rhs()->toConstant(); + MDefinition *operand = lhs()->isConstantValue() ? rhs() : lhs(); + const js::Value &val = lhs()->isConstantValue() ? lhs()->constantValue() : rhs()->constantValue(); if (operand->isToDouble() && operand->getOperand(0)->type() == MIRType_Int32) { - const js::Value &val = constant->value(); - // min(int32, cte >= INT32_MAX) = int32 if (val.isDouble() && val.toDouble() >= INT32_MAX && !isMax()) { MLimitedTruncate *limit = MLimitedTruncate::New(alloc, operand->getOperand(0), MDefinition::NoTruncate); block()->insertBefore(this, limit); MToDouble *toDouble = MToDouble::New(alloc, limit); block()->insertBefore(this, toDouble); return toDouble; @@ -1954,35 +1980,35 @@ MDiv::foldsTo(TempAllocator &alloc) void MDiv::analyzeEdgeCasesForward() { // This is only meaningful when doing integer division. if (specialization_ != MIRType_Int32) return; // Try removing divide by zero check - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) + if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0)) canBeDivideByZero_ = false; // If lhs is a constant int != INT32_MIN, then // negative overflow check can be skipped. - if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(INT32_MIN)) + if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(INT32_MIN)) canBeNegativeOverflow_ = false; // If rhs is a constant int != -1, likewise. - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(-1)) + if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(-1)) canBeNegativeOverflow_ = false; // If lhs is != 0, then negative zero check can be skipped. - if (lhs()->isConstant() && !lhs()->toConstant()->value().isInt32(0)) + if (lhs()->isConstantValue() && !lhs()->constantValue().isInt32(0)) setCanBeNegativeZero(false); // If rhs is >= 0, likewise. - if (rhs()->isConstant()) { - const js::Value &val = rhs()->toConstant()->value(); + if (rhs()->isConstantValue()) { + const js::Value &val = rhs()->constantValue(); if (val.isInt32() && val.toInt32() >= 0) setCanBeNegativeZero(false); } } void MDiv::analyzeEdgeCasesBackward() { @@ -2010,21 +2036,21 @@ MMod::foldsTo(TempAllocator &alloc) void MMod::analyzeEdgeCasesForward() { // These optimizations make sense only for integer division if (specialization_ != MIRType_Int32) return; - if (rhs()->isConstant() && !rhs()->toConstant()->value().isInt32(0)) + if (rhs()->isConstantValue() && !rhs()->constantValue().isInt32(0)) canBeDivideByZero_ = false; - if (rhs()->isConstant()) { - int32_t n = rhs()->toConstant()->value().toInt32(); + if (rhs()->isConstantValue()) { + int32_t n = rhs()->constantValue().toInt32(); if (n > 0 && !IsPowerOfTwo(n)) canBePowerOfTwoDivisor_ = false; } } bool MMod::fallible() const { @@ -2088,25 +2114,25 @@ void MMul::analyzeEdgeCasesForward() { // Try to remove the check for negative zero // This only makes sense when using the integer multiplication if (specialization() != MIRType_Int32) return; // If lhs is > 0, no need for negative zero check. - if (lhs()->isConstant()) { - const js::Value &val = lhs()->toConstant()->value(); + if (lhs()->isConstantValue()) { + const js::Value &val = lhs()->constantValue(); if (val.isInt32() && val.toInt32() > 0) setCanBeNegativeZero(false); } // If rhs is > 0, likewise. - if (rhs()->isConstant()) { - const js::Value &val = rhs()->toConstant()->value(); + if (rhs()->isConstantValue()) { + const js::Value &val = rhs()->constantValue(); if (val.isInt32() && val.toInt32() > 0) setCanBeNegativeZero(false); } } void MMul::analyzeEdgeCasesBackward() { @@ -2365,25 +2391,25 @@ MCompare::inputType() static inline bool MustBeUInt32(MDefinition *def, MDefinition **pwrapped) { if (def->isUrsh()) { *pwrapped = def->toUrsh()->getOperand(0); MDefinition *rhs = def->toUrsh()->getOperand(1); return !def->toUrsh()->bailoutsDisabled() - && rhs->isConstant() - && rhs->toConstant()->value().isInt32() - && rhs->toConstant()->value().toInt32() == 0; + && rhs->isConstantValue() + && rhs->constantValue().isInt32() + && rhs->constantValue().toInt32() == 0; } - if (def->isConstant()) { + if (def->isConstantValue()) { *pwrapped = def; - return def->toConstant()->value().isInt32() - && def->toConstant()->value().toInt32() >= 0; + return def->constantValue().isInt32() + && def->constantValue().toInt32() >= 0; } return false; } bool MBinaryInstruction::tryUseUnsignedOperands() { @@ -2548,17 +2574,17 @@ MDefinition * MBitNot::foldsTo(TempAllocator &alloc) { if (specialization_ != MIRType_Int32) return this; MDefinition *input = getOperand(0); if (input->isConstant()) { - js::Value v = Int32Value(~(input->toConstant()->value().toInt32())); + js::Value v = Int32Value(~(input->constantValue().toInt32())); return MConstant::New(alloc, v); } if (input->isBitNot() && input->toBitNot()->specialization_ == MIRType_Int32) { MOZ_ASSERT(input->toBitNot()->getOperand(0)->type() == MIRType_Int32); return input->toBitNot()->getOperand(0); // ~~x => x } @@ -2873,82 +2899,95 @@ MToInt32::analyzeEdgeCasesBackward() if (!NeedNegativeZeroCheck(this)) setCanBeNegativeZero(false); } MDefinition * MTruncateToInt32::foldsTo(TempAllocator &alloc) { MDefinition *input = getOperand(0); + if (input->isBox()) + input = input->getOperand(0); + if (input->type() == MIRType_Int32) return input; if (input->type() == MIRType_Double && input->isConstant()) { - const Value &v = input->toConstant()->value(); + const Value &v = input->constantValue(); int32_t ret = ToInt32(v.toDouble()); return MConstant::New(alloc, Int32Value(ret)); } return this; } MDefinition * MToDouble::foldsTo(TempAllocator &alloc) { - MDefinition *in = input(); - if (in->type() == MIRType_Double) - return in; - - if (in->isConstant()) { - const Value &v = in->toConstant()->value(); + MDefinition *input = getOperand(0); + if (input->isBox()) + input = input->getOperand(0); + + if (input->type() == MIRType_Double) + return input; + + if (input->isConstant()) { + const Value &v = input->toConstant()->value(); if (v.isNumber()) { double out = v.toNumber(); return MConstant::New(alloc, DoubleValue(out)); } } return this; } MDefinition * MToFloat32::foldsTo(TempAllocator &alloc) { - if (input()->type() == MIRType_Float32) - return input(); + MDefinition *input = getOperand(0); + if (input->isBox()) + input = input->getOperand(0); + + if (input->type() == MIRType_Float32) + return input; // If x is a Float32, Float32(Double(x)) == x - if (input()->isToDouble() && input()->toToDouble()->input()->type() == MIRType_Float32) - return input()->toToDouble()->input(); - - if (input()->isConstant()) { - const Value &v = input()->toConstant()->value(); + if (input->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32) + return input->toToDouble()->input(); + + if (input->isConstant()) { + const Value &v = input->toConstant()->value(); if (v.isNumber()) { float out = v.toNumber(); MConstant *c = MConstant::New(alloc, DoubleValue(out)); c->setResultType(MIRType_Float32); return c; } } return this; } MDefinition * MToString::foldsTo(TempAllocator &alloc) { MDefinition *in = input(); + if (in->isBox()) + in = in->getOperand(0); + if (in->type() == MIRType_String) return in; return this; } MDefinition * MClampToUint8::foldsTo(TempAllocator &alloc) { - if (input()->isConstant()) { - const Value &v = input()->toConstant()->value(); + if (input()->isConstantValue()) { + const Value &v = input()->constantValue(); if (v.isDouble()) { int32_t clamped = ClampDoubleToUint8(v.toDouble()); return MConstant::New(alloc, Int32Value(clamped)); } if (v.isInt32()) { int32_t clamped = ClampIntForUint8Array(v.toInt32()); return MConstant::New(alloc, Int32Value(clamped)); } @@ -3353,18 +3392,18 @@ MNot::cacheOperandMightEmulateUndefined( if (!MaybeEmulatesUndefined(getOperand(0))) markOperandCantEmulateUndefined(); } MDefinition * MNot::foldsTo(TempAllocator &alloc) { // Fold if the input is constant - if (input()->isConstant()) { - bool result = input()->toConstant()->valueToBoolean(); + if (input()->isConstantValue() && !input()->constantValue().isMagic()) { + bool result = input()->constantToBoolean(); if (type() == MIRType_Int32) return MConstant::New(alloc, Int32Value(!result)); // ToBoolean can't cause side effects, so this is safe. return MConstant::New(alloc, BooleanValue(!result)); } // If the operand of the Not is itself a Not, they cancel out. But we can't @@ -3920,30 +3959,30 @@ MGetPropertyCache::updateForReplacement( MGetPropertyCache *other = ins->toGetPropertyCache(); location_.append(&other->location_); return true; } MDefinition * MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc) { - if (input()->isConstant()) { - const Value &v = input()->toConstant()->value(); + if (input()->isConstantValue()) { + const Value &v = input()->constantValue(); if (v.isInt32()) return MConstant::New(alloc, DoubleValue(uint32_t(v.toInt32()))); } return this; } MDefinition * MAsmJSUnsignedToFloat32::foldsTo(TempAllocator &alloc) { - if (input()->isConstant()) { - const Value &v = input()->toConstant()->value(); + if (input()->isConstantValue()) { + const Value &v = input()->constantValue(); if (v.isInt32()) { double dval = double(uint32_t(v.toInt32())); if (IsFloat32Representable(dval)) return MConstant::NewAsmJS(alloc, JS::Float32Value(float(dval)), MIRType_Float32); } } return this; @@ -3982,32 +4021,32 @@ MSqrt::trySpecializeFloat32(TempAllocato setResultType(MIRType_Float32); setPolicyType(MIRType_Float32); } MDefinition * MClz::foldsTo(TempAllocator &alloc) { - if (num()->isConstant()) { - int32_t n = num()->toConstant()->value().toInt32(); + if (num()->isConstantValue()) { + int32_t n = num()->constantValue().toInt32(); if (n == 0) return MConstant::New(alloc, Int32Value(32)); return MConstant::New(alloc, Int32Value(mozilla::CountLeadingZeroes32(n))); } return this; } MDefinition * MBoundsCheck::foldsTo(TempAllocator &alloc) { - if (index()->isConstant() && length()->isConstant()) { - uint32_t len = length()->toConstant()->value().toInt32(); - uint32_t idx = index()->toConstant()->value().toInt32(); + if (index()->isConstantValue() && length()->isConstantValue()) { + uint32_t len = length()->constantValue().toInt32(); + uint32_t idx = index()->constantValue().toInt32(); if (idx + uint32_t(minimum()) < len && idx + uint32_t(maximum()) < len) return index(); } return this; } MDefinition *
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -747,16 +747,23 @@ class MDefinition : public MNode return this->to<M##opcode>(); \ } \ const M##opcode *to##opcode() const { \ return this->to<M##opcode>(); \ } MIR_OPCODE_LIST(OPCODE_CASTS) # undef OPCODE_CASTS + bool isConstantValue() { + return isConstant() || (isBox() && getOperand(0)->isConstant()); + } + const Value &constantValue(); + const Value *constantVp(); + bool constantToBoolean(); + inline MInstruction *toInstruction(); inline const MInstruction *toInstruction() const; bool isInstruction() const { return !isPhi(); } virtual bool isControlInstruction() const { return false;
--- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -150,16 +150,19 @@ RangeAnalysis::addBetaNodes() BranchDirection branch_dir; MTest *test = block->immediateDominatorBranch(&branch_dir); if (!test || !test->getOperand(0)->isCompare()) continue; MCompare *compare = test->getOperand(0)->toCompare(); + if (compare->compareType() == MCompare::Compare_Unknown) + continue; + // TODO: support unsigned comparisons if (compare->compareType() == MCompare::Compare_UInt32) continue; MDefinition *left = compare->getOperand(0); MDefinition *right = compare->getOperand(1); double bound; double conservativeLower = NegativeInfinity<double>(); @@ -169,22 +172,22 @@ RangeAnalysis::addBetaNodes() JSOp jsop = compare->jsop(); if (branch_dir == FALSE_BRANCH) { jsop = NegateCompareOp(jsop); conservativeLower = GenericNaN(); conservativeUpper = GenericNaN(); } - if (left->isConstant() && left->toConstant()->value().isNumber()) { - bound = left->toConstant()->value().toNumber(); + if (left->isConstantValue() && left->constantValue().isNumber()) { + bound = left->constantValue().toNumber(); val = right; jsop = ReverseCompareOp(jsop); - } else if (right->isConstant() && right->toConstant()->value().isNumber()) { - bound = right->toConstant()->value().toNumber(); + } else if (right->isConstantValue() && right->constantValue().isNumber()) { + bound = right->constantValue().toNumber(); val = left; } else if (left->type() == MIRType_Int32 && right->type() == MIRType_Int32) { MDefinition *smaller = nullptr; MDefinition *greater = nullptr; if (jsop == JSOP_LT) { smaller = left; greater = right; } else if (jsop == JSOP_GT) { @@ -1305,42 +1308,42 @@ MBitNot::computeRange(TempAllocator &all void MLsh::computeRange(TempAllocator &alloc) { Range left(getOperand(0)); Range right(getOperand(1)); left.wrapAroundToInt32(); MDefinition *rhs = getOperand(1); - if (!rhs->isConstant()) { - right.wrapAroundToShiftCount(); - setRange(Range::lsh(alloc, &left, &right)); + if (rhs->isConstantValue() && rhs->constantValue().isInt32()) { + int32_t c = rhs->constantValue().toInt32(); + setRange(Range::lsh(alloc, &left, c)); return; } - int32_t c = rhs->toConstant()->value().toInt32(); - setRange(Range::lsh(alloc, &left, c)); + right.wrapAroundToShiftCount(); + setRange(Range::lsh(alloc, &left, &right)); } void MRsh::computeRange(TempAllocator &alloc) { Range left(getOperand(0)); Range right(getOperand(1)); left.wrapAroundToInt32(); MDefinition *rhs = getOperand(1); - if (!rhs->isConstant()) { - right.wrapAroundToShiftCount(); - setRange(Range::rsh(alloc, &left, &right)); + if (rhs->isConstantValue() && rhs->constantValue().isInt32()) { + int32_t c = rhs->constantValue().toInt32(); + setRange(Range::rsh(alloc, &left, c)); return; } - int32_t c = rhs->toConstant()->value().toInt32(); - setRange(Range::rsh(alloc, &left, c)); + right.wrapAroundToShiftCount(); + setRange(Range::rsh(alloc, &left, &right)); } void MUrsh::computeRange(TempAllocator &alloc) { Range left(getOperand(0)); Range right(getOperand(1)); @@ -1348,21 +1351,21 @@ MUrsh::computeRange(TempAllocator &alloc // can be thought of as converting its left operand to int32, and then // reinterpreting the int32 bits as a uint32 value. Both approaches yield // the same result. Since we lack support for full uint32 ranges, we use // the second interpretation, though it does cause us to be conservative. left.wrapAroundToInt32(); right.wrapAroundToShiftCount(); MDefinition *rhs = getOperand(1); - if (!rhs->isConstant()) { - setRange(Range::ursh(alloc, &left, &right)); + if (rhs->isConstantValue() && rhs->constantValue().isInt32()) { + int32_t c = rhs->constantValue().toInt32(); + setRange(Range::ursh(alloc, &left, c)); } else { - int32_t c = rhs->toConstant()->value().toInt32(); - setRange(Range::ursh(alloc, &left, c)); + setRange(Range::ursh(alloc, &left, &right)); } MOZ_ASSERT(range()->lower() >= 0); } void MAbs::computeRange(TempAllocator &alloc) { @@ -2729,17 +2732,17 @@ CloneForDeadBranches(TempAllocator &allo clone->setRange(nullptr); // Set UseRemoved flag on the cloned instruction in order to chain recover // instruction for the bailout path. clone->setUseRemovedUnchecked(); candidate->block()->insertBefore(candidate, clone); - if (!candidate->isConstant()) { + if (!candidate->isConstantValue()) { MOZ_ASSERT(clone->canRecoverOnBailout()); clone->setRecoveredOnBailout(); } // Replace the candidate by its recovered on bailout clone within recovered // instructions and resume points operands. for (MUseIterator i(candidate->usesBegin()); i != candidate->usesEnd(); ) { MUse *use = *i++;
--- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -528,20 +528,20 @@ static bool IndexOf(MDefinition *ins, int32_t *res) { MOZ_ASSERT(ins->isLoadElement() || ins->isStoreElement()); MDefinition *indexDef = ins->getOperand(1); // ins->index(); if (indexDef->isBoundsCheck()) indexDef = indexDef->toBoundsCheck()->index(); if (indexDef->isToInt32()) indexDef = indexDef->toToInt32()->getOperand(0); - if (!indexDef->isConstant()) + if (!indexDef->isConstantValue()) return false; - Value index = indexDef->toConstant()->value(); + Value index = indexDef->constantValue(); if (!index.isInt32()) return false; *res = index.toInt32(); return true; } // Returns False if the array is not escaped and if it is optimizable by // ScalarReplacementOfArray. @@ -961,17 +961,17 @@ ArrayMemoryView::visitSetInitializedLeng if (!isArrayStateElements(elements)) return; // Replace by the new initialized length. Note that the argument of // MSetInitalizedLength is the last index and not the initialized length. // To obtain the length, we need to add 1 to it, and thus we need to create // a new constant that we register in the ArrayState. state_ = BlockState::Copy(alloc_, state_); - int32_t initLengthValue = ins->index()->toConstant()->value().toInt32() + 1; + int32_t initLengthValue = ins->index()->constantValue().toInt32() + 1; MConstant *initLength = MConstant::New(alloc_, Int32Value(initLengthValue)); ins->block()->insertBefore(ins, initLength); ins->block()->insertBefore(ins, state_); state_->setInitializedLength(initLength); // Remove original instruction. discardInstruction(ins, elements); }
--- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -487,37 +487,37 @@ LIRGeneratorARM::visitAsmJSUnsignedToFlo void LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); LAllocation ptrAlloc; // For the ARM it is best to keep the 'ptr' in a register if a bounds check is needed. - if (ptr->isConstant() && !ins->needsBoundsCheck()) { + if (ptr->isConstantValue() && !ins->needsBoundsCheck()) { // A bounds check is only skipped for a positive index. - MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + MOZ_ASSERT(ptr->constantValue().toInt32() >= 0); + ptrAlloc = LAllocation(ptr->constantVp()); } else { ptrAlloc = useRegisterAtStart(ptr); } define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins); } void LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) { MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); LAllocation ptrAlloc; - if (ptr->isConstant() && !ins->needsBoundsCheck()) { - MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + if (ptr->isConstantValue() && !ins->needsBoundsCheck()) { + MOZ_ASSERT(ptr->constantValue().toInt32() >= 0); + ptrAlloc = LAllocation(ptr->constantVp()); } else { ptrAlloc = useRegisterAtStart(ptr); } add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins); } void
--- a/js/src/jit/mips/Lowering-mips.cpp +++ b/js/src/jit/mips/Lowering-mips.cpp @@ -465,37 +465,37 @@ void LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins) { MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); LAllocation ptrAlloc; // For MIPS it is best to keep the 'ptr' in a register if a bounds check // is needed. - if (ptr->isConstant() && !ins->needsBoundsCheck()) { - int32_t ptrValue = ptr->toConstant()->value().toInt32(); + if (ptr->isConstantValue() && !ins->needsBoundsCheck()) { + int32_t ptrValue = ptr->constantValue().toInt32(); // A bounds check is only skipped for a positive index. MOZ_ASSERT(ptrValue >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + ptrAlloc = LAllocation(ptr->constantVp()); } else ptrAlloc = useRegisterAtStart(ptr); define(new(alloc()) LAsmJSLoadHeap(ptrAlloc), ins); } void LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins) { MDefinition *ptr = ins->ptr(); MOZ_ASSERT(ptr->type() == MIRType_Int32); LAllocation ptrAlloc; - if (ptr->isConstant() && !ins->needsBoundsCheck()) { - MOZ_ASSERT(ptr->toConstant()->value().toInt32() >= 0); - ptrAlloc = LAllocation(ptr->toConstant()->vp()); + if (ptr->isConstantValue() && !ins->needsBoundsCheck()) { + MOZ_ASSERT(ptr->constantValue().toInt32() >= 0); + ptrAlloc = LAllocation(ptr->constantVp()); } else ptrAlloc = useRegisterAtStart(ptr); add(new(alloc()) LAsmJSStoreHeap(ptrAlloc, useRegisterAtStart(ins->value())), ins); } void LIRGeneratorMIPS::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr *ins)
--- a/js/src/jsapi-tests/testJitRangeAnalysis.cpp +++ b/js/src/jsapi-tests/testJitRangeAnalysis.cpp @@ -122,16 +122,17 @@ BEGIN_TEST(testJitRangeAnalysis_MathSign // if (p < 0) MParameter *p = func.createParameter(); entry->add(p); MConstant *c0 = MConstant::New(func.alloc, DoubleValue(0.0)); entry->add(c0); MConstant *cm0 = MConstant::New(func.alloc, DoubleValue(-0.0)); entry->add(cm0); MCompare *cmp = MCompare::New(func.alloc, p, c0, JSOP_LT); + cmp->setCompareType(MCompare::Compare_Double); entry->add(cmp); entry->end(MTest::New(func.alloc, cmp, thenBlock, elseBlock)); // { // return Math.sign(p + -0); // } MAdd *thenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double); thenBlock->add(thenAdd); @@ -139,16 +140,17 @@ BEGIN_TEST(testJitRangeAnalysis_MathSign thenBlock->add(thenSign); MReturn *thenRet = MReturn::New(func.alloc, thenSign); thenBlock->end(thenRet); // else // { // if (p >= 0) MCompare *elseCmp = MCompare::New(func.alloc, p, c0, JSOP_GE); + elseCmp->setCompareType(MCompare::Compare_Double); elseBlock->add(elseCmp); elseBlock->end(MTest::New(func.alloc, elseCmp, elseThenBlock, elseElseBlock)); // { // return Math.sign(p + -0); // } MAdd *elseThenAdd = MAdd::NewAsmJS(func.alloc, p, cm0, MIRType_Double); elseThenBlock->add(elseThenAdd);
deleted file mode 100644 --- a/js/src/tests/ecma_7/SIMD/coercions.js +++ /dev/null @@ -1,66 +0,0 @@ -// |reftest| skip-if(!this.hasOwnProperty("SIMD")) -var BUGNUMBER = 1061229; -var float32x4 = SIMD.float32x4; -var int32x4 = SIMD.int32x4; -var {StructType, int32} = TypedObject; -var summary = 'constructors used as coercions'; - -/* - * Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/licenses/publicdomain/ - */ - -function assertCaught(f) { - var caught = false; - var args = Array.slice(arguments, 1); - try { - f.apply(null, args); - } catch (e) { - caught = true; - print(e) - } - assertEq(caught, true); -} - -function test() { - var x = int32x4(1, 2, 3, 4); - var y = int32x4(x); - - assertEq(x, y); - - assertEq(y.x, x.x); - assertEq(y.x, 1); - assertEq(y.y, x.y); - assertEq(y.y, 2); - assertEq(y.z, x.z); - assertEq(y.z, 3); - assertEq(y.w, x.w); - assertEq(y.w, 4); - - assertCaught(int32x4, 3); - assertCaught(int32x4, float32x4(1, 2, 3, 4)); - assertCaught(int32x4, 'pony x 4'); - - var x = float32x4(NaN, 13.37, -Infinity, 4); - var y = float32x4(x); - - assertEq(x, y); - - assertEq(y.x, x.x); - assertEq(y.x, Math.fround(NaN)); - assertEq(y.y, x.y); - assertEq(y.y, Math.fround(13.37)); - assertEq(y.z, x.z); - assertEq(y.z, Math.fround(-Infinity)); - assertEq(y.w, x.w); - assertEq(y.w, Math.fround(4)); - - assertCaught(float32x4, 3); - assertCaught(float32x4, int32x4(1, 2, 3, 4)); - assertCaught(float32x4, 'pony x 4'); - - if (typeof reportCompare === "function") - reportCompare(true, true); -} - -test();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/constructors.js @@ -0,0 +1,66 @@ +// |reftest| skip-if(!this.hasOwnProperty("SIMD")) + +var float32x4 = SIMD.float32x4; +var int32x4 = SIMD.int32x4; + +function test() { + + // Constructors. + assertEqX4(int32x4(1, 2, 3, 4), [1,2,3,4]); + assertEqX4(int32x4(1, 2, 3), [1,2,3,0]); + assertEqX4(int32x4(1, 2), [1,2,0,0]); + // The 1-argument form is reserved for coercions. + assertEqX4(int32x4(), [0,0,0,0]); + assertEqX4(int32x4(1, 2, 3, 4, 5), [1,2,3,4]); + assertEqX4(int32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]); + + assertEqX4(float32x4(1, 2, 3, 4), [1,2,3,4]); + assertEqX4(float32x4(1, 2, 3), [1,2,3,NaN]); + assertEqX4(float32x4(1, 2), [1,2,NaN,NaN]); + // The 1-argument form is reserved for coercions. + assertEqX4(float32x4(), [NaN,NaN,NaN,NaN]); + assertEqX4(float32x4(1, 2, 3, 4, 5), [1,2,3,4]); + assertEqX4(float32x4(1, 2, 3, 4, 5, 6), [1,2,3,4]); + + // Constructors used as coercion. + var x = int32x4(1, 2, 3, 4); + var y = int32x4(x); + + assertEq(x, y); + + assertEq(y.x, x.x); + assertEq(y.x, 1); + assertEq(y.y, x.y); + assertEq(y.y, 2); + assertEq(y.z, x.z); + assertEq(y.z, 3); + assertEq(y.w, x.w); + assertEq(y.w, 4); + + assertThrowsInstanceOf(() => int32x4(3), TypeError); + assertThrowsInstanceOf(() => int32x4(float32x4(1,2,3,4)), TypeError); + assertThrowsInstanceOf(() => int32x4('pony x 4'), TypeError); + + var x = float32x4(NaN, 13.37, -Infinity, 4); + var y = float32x4(x); + + assertEq(x, y); + + assertEq(y.x, x.x); + assertEq(y.x, Math.fround(NaN)); + assertEq(y.y, x.y); + assertEq(y.y, Math.fround(13.37)); + assertEq(y.z, x.z); + assertEq(y.z, Math.fround(-Infinity)); + assertEq(y.w, x.w); + assertEq(y.w, Math.fround(4)); + + assertThrowsInstanceOf(() => float32x4(3), TypeError); + assertThrowsInstanceOf(() => float32x4(int32x4(1,2,3,4)), TypeError); + assertThrowsInstanceOf(() => float32x4('pony x 4'), TypeError); + + if (typeof reportCompare === "function") + reportCompare(true, true); +} + +test();
new file mode 100644 --- /dev/null +++ b/js/src/tests/ecma_7/SIMD/signmask.js @@ -0,0 +1,33 @@ +// |reftest| skip-if(!this.hasOwnProperty("SIMD")) + +var float32x4 = SIMD.float32x4; +var int32x4 = SIMD.int32x4; + +function test_float32x4() { + var v, w; + for ([v, w] of [[float32x4(-1, 20, 30, 4), 0b0001], + [float32x4(9.999, 2.1234, 30.4443, -4), 0b1000], + [float32x4(0, -Infinity, +Infinity, -0), 0b1010]]) + { + assertEq(v.signMask, w); + } + + if (typeof reportCompare === "function") + reportCompare(true, true); +} + +function test_int32x4() { + var v, w; + for ([v, w] of [[int32x4(-1, 20, 30, 4), 0b0001], + [int32x4(10, 2, 30.2, -4), 0b1000], + [int32x4(0, 0x80000000, 0x7fffffff, -0), 0b0010]]) + { + assertEq(v.signMask, w); + } + + if (typeof reportCompare === "function") + reportCompare(true, true); +} + +test_float32x4(); +test_int32x4();
new file mode 100644 --- /dev/null +++ b/layout/generic/RubyUtils.cpp @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "RubyUtils.h" +#include "nsIFrame.h" + +using namespace mozilla; + +NS_DECLARE_FRAME_PROPERTY(ReservedISize, nullptr); + +union NSCoordValue +{ + nscoord mCoord; + void* mPointer; + static_assert(sizeof(nscoord) <= sizeof(void*), + "Cannot store nscoord in pointer"); +}; + +/* static */ void +RubyUtils::SetReservedISize(nsIFrame* aFrame, nscoord aISize) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + NSCoordValue value = { aISize }; + aFrame->Properties().Set(ReservedISize(), value.mPointer); +} + +/* static */ void +RubyUtils::ClearReservedISize(nsIFrame* aFrame) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + aFrame->Properties().Remove(ReservedISize()); +} + +/* static */ nscoord +RubyUtils::GetReservedISize(nsIFrame* aFrame) +{ + MOZ_ASSERT(IsExpandableRubyBox(aFrame)); + NSCoordValue value; + value.mPointer = aFrame->Properties().Get(ReservedISize()); + return value.mCoord; +}
new file mode 100644 --- /dev/null +++ b/layout/generic/RubyUtils.h @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 mozilla_RubyUtils_h_ +#define mozilla_RubyUtils_h_ + +#include "nsGkAtoms.h" +#include "nsIFrame.h" + +namespace mozilla { + +/** + * Reserved ISize + * + * With some exceptions, each ruby internal box has two isizes, which + * are the reflowed isize and the final isize. The reflowed isize is + * what a box itself needs. It is determined when the box gets reflowed. + * + * The final isize is what a box should be as the final result. For a + * ruby base/text box, the final isize is the size of its ruby column. + * For a ruby base/text container, the final isize is the size of its + * ruby segment. The final isize is never smaller than the reflowed + * isize. It is initially determined when a ruby column/segment gets + * fully reflowed, and may be advanced when a box is expanded, e.g. + * for justification. + * + * The difference between the reflowed isize and the final isize is + * reserved in the line layout after reflowing a box, hence it is called + * "Reserved ISize" here. It is used to expand the ruby boxes from their + * reflowed isize to the final isize during alignment of the line. + * + * There are three exceptions for the final isize: + * 1. A ruby text container has a larger final isize only if it is for + * a span or collapsed annotations. + * 2. A ruby base container has a larger final isize only if at least + * one of its ruby text containers does. + * 3. If a ruby text container has a larger final isize, its children + * must not have. + */ + +class RubyUtils +{ +public: + static inline bool IsExpandableRubyBox(nsIFrame* aFrame) + { + nsIAtom* type = aFrame->GetType(); + return type == nsGkAtoms::rubyBaseFrame || + type == nsGkAtoms::rubyTextFrame || + type == nsGkAtoms::rubyBaseContainerFrame || + type == nsGkAtoms::rubyTextContainerFrame; + } + + static void SetReservedISize(nsIFrame* aFrame, nscoord aISize); + static void ClearReservedISize(nsIFrame* aFrame); + static nscoord GetReservedISize(nsIFrame* aFrame); +}; + +} + +#endif /* !defined(mozilla_RubyUtils_h_) */
--- a/layout/generic/moz.build +++ b/layout/generic/moz.build @@ -89,16 +89,17 @@ UNIFIED_SOURCES += [ 'nsSimplePageSequenceFrame.cpp', 'nsSplittableFrame.cpp', 'nsSubDocumentFrame.cpp', 'nsTextFrame.cpp', 'nsTextFrameUtils.cpp', 'nsTextRunTransformations.cpp', 'nsVideoFrame.cpp', 'nsViewportFrame.cpp', + 'RubyUtils.cpp', 'ScrollbarActivity.cpp', 'StickyScrollContainer.cpp', 'TextOverflow.cpp', ] # nsLineLayout.cpp needs to be built separately because it uses plarena.h. # nsPluginFrame.cpp needs to be built separately because of name clashes in the OS X headers. SOURCES += [
--- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -757,18 +757,22 @@ public: /** * Return frame's position without relative positioning */ nsPoint GetNormalPosition() const; mozilla::LogicalPoint GetLogicalNormalPosition(mozilla::WritingMode aWritingMode, nscoord aContainerWidth) const { + // Subtract the width of this frame from the container width to get + // the correct position in rtl frames where the origin is on the + // right instead of the left return mozilla::LogicalPoint(aWritingMode, - GetNormalPosition(), aContainerWidth); + GetNormalPosition(), + aContainerWidth - mRect.width); } virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild) { return aChild->GetPosition(); } nsPoint GetPositionIgnoringScrolling(); static void DestroyRegion(void* aPropertyValue);
--- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -19,16 +19,17 @@ #include "nsStyleContext.h" #include "nsPresContext.h" #include "nsGkAtoms.h" #include "nsIContent.h" #include "nsLayoutUtils.h" #include "nsTextFrame.h" #include "nsStyleStructInlines.h" #include "nsBidiPresUtils.h" +#include "RubyUtils.h" #include <algorithm> #ifdef DEBUG #undef NOISY_INLINEDIR_ALIGN #undef NOISY_BLOCKDIR_ALIGN #undef REALLY_NOISY_BLOCKDIR_ALIGN #undef NOISY_REFLOW #undef REALLY_NOISY_REFLOW @@ -72,17 +73,18 @@ nsLineLayout::nsLineLayout(nsPresContext mLineIsEmpty(false), mLineEndsInBR(false), mNeedBackup(false), mInFirstLine(false), mGotLineBox(false), mInFirstLetter(false), mHasBullet(false), mDirtyNextLine(false), - mLineAtStart(false) + mLineAtStart(false), + mHasRuby(false) { MOZ_ASSERT(aOuterReflowState, "aOuterReflowState must not be null"); NS_ASSERTION(aFloatManager || aOuterReflowState->frame->GetType() == nsGkAtoms::letterFrame, "float manager should be present"); MOZ_COUNT_CTOR(nsLineLayout); // Stash away some style data that we need @@ -91,16 +93,19 @@ nsLineLayout::nsLineLayout(nsPresContext mStyleText = blockFrame->StyleTextForLineLayout(); else mStyleText = aOuterReflowState->frame->StyleText(); mLineNumber = 0; mTotalPlacedFrames = 0; mBStartEdge = 0; mTrimmableISize = 0; +#ifdef DEBUG + mFinalLineBSize = nscoord_MIN; +#endif mInflationMinFontSize = nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame); // Instead of always pre-initializing the free-lists for frames and // spans, we do it on demand so that situations that only use a few // frames and spans won't waste a lot of time in unneeded // initialization. @@ -188,21 +193,16 @@ nsLineLayout::BeginLineReflow(nscoord aI PerSpanData* psd = NewPerSpanData(); mCurrentSpan = mRootSpan = psd; psd->mReflowState = mBlockReflowState; psd->mIStart = aICoord; psd->mICoord = aICoord; psd->mIEnd = aICoord + aISize; mContainerWidth = aContainerWidth; - PerFrameData* pfd = NewPerFrameData(mBlockReflowState->frame); - pfd->mAscent = 0; - pfd->mSpan = psd; - psd->mFrame = pfd; - // If we're in a constrained height frame, then we don't allow a // max line box width to take effect. if (!(LineContainerFrame()->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT)) { // If the available size is greater than the maximum line box width (if // specified), then we need to adjust the line box width to be at the max // possible width. @@ -236,16 +236,21 @@ nsLineLayout::BeginLineReflow(nscoord aI } } nscoord indent = nsRuleNode::ComputeCoordPercentCalc(textIndent, pctBasis); mTextIndent = indent; psd->mICoord += indent; } + + PerFrameData* pfd = NewPerFrameData(mBlockReflowState->frame); + pfd->mAscent = 0; + pfd->mSpan = psd; + psd->mFrame = pfd; } void nsLineLayout::EndLineReflow() { #ifdef NOISY_REFLOW nsFrame::ListTag(stdout, mBlockReflowState->frame); printf(": EndLineReflow: width=%d\n", mRootSpan->mICoord - mRootSpan->mIStart); @@ -461,22 +466,22 @@ nsLineLayout::EndSpan(nsIFrame* aFrame) void nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData* aFrame) { NS_PRECONDITION(this != mBaseLineLayout, "This method must not be called in a base line layout."); PerFrameData* baseFrame = mBaseLineLayout->LastFrame(); MOZ_ASSERT(aFrame && baseFrame); - MOZ_ASSERT(!aFrame->GetFlag(PFD_ISLINKEDTOBASE), + MOZ_ASSERT(!aFrame->mIsLinkedToBase, "The frame must not have been linked with the base"); aFrame->mNextAnnotation = baseFrame->mNextAnnotation; baseFrame->mNextAnnotation = aFrame; - aFrame->SetFlag(PFD_ISLINKEDTOBASE, true); + aFrame->mIsLinkedToBase = true; } int32_t nsLineLayout::GetCurrentSpanCount() const { NS_ASSERTION(mCurrentSpan == mRootSpan, "bad linelayout user"); int32_t count = 0; PerFrameData* pfd = mRootSpan->mFirstFrame; @@ -554,17 +559,17 @@ nsLineLayout::PushFrame(nsIFrame* aFrame #endif } void nsLineLayout::UnlinkFrame(PerFrameData* pfd) { while (nullptr != pfd) { PerFrameData* next = pfd->mNext; - if (pfd->GetFlag(PFD_ISLINKEDTOBASE)) { + if (pfd->mIsLinkedToBase) { // This frame is linked to a ruby base, and should not be freed // now. Just unlink it from the span. It will be freed when its // base frame gets unlinked. pfd->mNext = pfd->mPrev = nullptr; pfd = next; continue; } @@ -641,19 +646,30 @@ nsLineLayout::NewPerFrameData(nsIFrame* } else { mBaseLineLayout->mFrameFreeList = pfd->mNext; } pfd->mSpan = nullptr; pfd->mNext = nullptr; pfd->mPrev = nullptr; pfd->mNextAnnotation = nullptr; - pfd->mFlags = 0; // all flags default to false pfd->mFrame = aFrame; + // all flags default to false + pfd->mRelativePos = false; + pfd->mIsTextFrame = false; + pfd->mIsNonEmptyTextFrame = false; + pfd->mIsNonWhitespaceTextFrame = false; + pfd->mIsLetterFrame = false; + pfd->mRecomputeOverflow = false; + pfd->mIsBullet = false; + pfd->mSkipWhenTrimmingWhitespace = false; + pfd->mIsEmpty = false; + pfd->mIsLinkedToBase = false; + WritingMode frameWM = aFrame->GetWritingMode(); WritingMode lineWM = mRootSpan->mWritingMode; pfd->mBounds = LogicalRect(lineWM); pfd->mMargin = LogicalMargin(lineWM); pfd->mBorderPadding = LogicalMargin(lineWM); pfd->mOffsets = LogicalMargin(frameWM); pfd->mJustificationInfo = JustificationInfo(); @@ -851,19 +867,19 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra if (reflowState.ComputedISize() == NS_UNCONSTRAINEDSIZE) { reflowState.AvailableISize() = availableSpaceOnLine; } WritingMode stateWM = reflowState.GetWritingMode(); pfd->mMargin = reflowState.ComputedLogicalMargin().ConvertTo(lineWM, stateWM); pfd->mBorderPadding = reflowState.ComputedLogicalBorderPadding().ConvertTo(lineWM, stateWM); - pfd->SetFlag(PFD_RELATIVEPOS, - reflowState.mStyleDisplay->IsRelativelyPositionedStyle()); - if (pfd->GetFlag(PFD_RELATIVEPOS)) { + pfd->mRelativePos = + reflowState.mStyleDisplay->IsRelativelyPositionedStyle(); + if (pfd->mRelativePos) { pfd->mOffsets = reflowState.ComputedLogicalOffsets().ConvertTo(frameWM, stateWM); } // Calculate whether the the frame should have a start margin and // subtract the margin from the available width if necessary. // The margin will be applied to the starting inline coordinates of // the frame in CanPlaceFrame() after reflowing the frame. @@ -907,28 +923,29 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra aFrame->Reflow(mPresContext, metrics, *reflowStateHolder, aReflowStatus); } else { static_cast<nsTextFrame*>(aFrame)-> ReflowText(*this, availableSpaceOnLine, psd->mReflowState->rendContext, metrics, aReflowStatus); } pfd->mJustificationInfo = mJustificationInfo; + mJustificationInfo = JustificationInfo(); // See if the frame is a placeholderFrame and if it is process // the float. At the same time, check if the frame has any non-collapsed-away // content. bool placedFloat = false; bool isEmpty; if (!frameType) { isEmpty = pfd->mFrame->IsEmpty(); } else { if (nsGkAtoms::placeholderFrame == frameType) { isEmpty = true; - pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true); + pfd->mSkipWhenTrimmingWhitespace = true; nsIFrame* outOfFlowFrame = nsLayoutUtils::GetFloatFromPlaceholder(aFrame); if (outOfFlowFrame) { // Add mTrimmableISize to the available width since if the line ends // here, the width of the inline content will be reduced by // mTrimmableISize. nscoord availableISize = psd->mIEnd - (psd->mICoord - mTrimmableISize); if (psd->mNoWrap) { // If we place floats after inline content where there's @@ -945,45 +962,44 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra placedFloat = mBaseLineLayout->AddFloat(outOfFlowFrame, availableISize); NS_ASSERTION(!(outOfFlowFrame->GetType() == nsGkAtoms::letterFrame && GetFirstLetterStyleOK()), "FirstLetterStyle set on line with floating first letter"); } } else if (isText) { // Note non-empty text-frames for inline frame compatibility hackery - pfd->SetFlag(PFD_ISTEXTFRAME, true); + pfd->mIsTextFrame = true; nsTextFrame* textFrame = static_cast<nsTextFrame*>(pfd->mFrame); isEmpty = !textFrame->HasNoncollapsedCharacters(); if (!isEmpty) { - pfd->SetFlag(PFD_ISNONEMPTYTEXTFRAME, true); + pfd->mIsNonEmptyTextFrame = true; nsIContent* content = textFrame->GetContent(); const nsTextFragment* frag = content->GetText(); if (frag) { - pfd->SetFlag(PFD_ISNONWHITESPACETEXTFRAME, - !content->TextIsOnlyWhitespace()); + pfd->mIsNonWhitespaceTextFrame = !content->TextIsOnlyWhitespace(); } } } else if (nsGkAtoms::brFrame == frameType) { - pfd->SetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE, true); + pfd->mSkipWhenTrimmingWhitespace = true; isEmpty = false; } else { if (nsGkAtoms::letterFrame==frameType) { - pfd->SetFlag(PFD_ISLETTERFRAME, true); + pfd->mIsLetterFrame = true; } if (pfd->mSpan) { isEmpty = !pfd->mSpan->mHasNonemptyContent && pfd->mFrame->IsSelfEmpty(); } else { isEmpty = pfd->mFrame->IsEmpty(); } } } - pfd->SetFlag(PFD_ISEMPTY, isEmpty); + pfd->mIsEmpty = isEmpty; mFloatManager->Untranslate(oldWM, tPt); NS_ASSERTION(metrics.ISize(lineWM) >= 0, "bad inline size"); NS_ASSERTION(metrics.BSize(lineWM) >= 0,"bad block size"); if (metrics.ISize(lineWM) < 0) { metrics.ISize(lineWM) = 0; } @@ -1048,17 +1064,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra } } // Check whether this frame breaks up text runs. All frames break up text // runs (hence return false here) except for text frames and inline containers. bool continuingTextRun = aFrame->CanContinueTextRun(); // Clear any residual mTrimmableISize if this isn't a text frame - if (!continuingTextRun && !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) { + if (!continuingTextRun && !pfd->mSkipWhenTrimmingWhitespace) { mTrimmableISize = 0; } // See if we can place the frame. If we can't fit it, then we // return now. bool optionalBreakAfterFits; NS_ASSERTION(isText || !reflowStateHolder->IsFloating(), @@ -1069,16 +1085,19 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra aReflowStatus, &optionalBreakAfterFits)) { if (!isEmpty) { psd->mHasNonemptyContent = true; mLineIsEmpty = false; if (!pfd->mSpan) { // nonempty leaf content has been placed mLineAtStart = false; } + if (nsGkAtoms::rubyFrame == frameType) { + mHasRuby = true; + } } // Place the frame, updating aBounds with the final size and // location. Then apply the bottom+right margins (as // appropriate) to the frame. PlaceFrame(pfd, metrics); PerSpanData* span = pfd->mSpan; if (span) { @@ -1215,17 +1234,17 @@ nsLineLayout::CanPlaceFrame(PerFrameData * However, none of that applies if this is a letter frame (XXXbz why?) * * For box-decoration-break:clone we apply the end margin on all * continuations (that are not letter frames). */ if ((NS_FRAME_IS_NOT_COMPLETE(aStatus) || pfd->mFrame->LastInFlow()->GetNextContinuation() || pfd->mFrame->FrameIsNonLastInIBSplit()) && - !pfd->GetFlag(PFD_ISLETTERFRAME) && + !pfd->mIsLetterFrame && pfd->mFrame->StyleBorder()->mBoxDecorationBreak == NS_STYLE_BOX_DECORATION_BREAK_SLICE) { pfd->mMargin.IEnd(lineWM) = 0; } // Apply the start margin to the frame bounds. nscoord startMargin = pfd->mMargin.IStart(lineWM); nscoord endMargin = pfd->mMargin.IEnd(lineWM); @@ -1390,17 +1409,17 @@ nsLineLayout::AddBulletFrame(nsIFrame* a if (!static_cast<nsBlockFrame*>(blockFrame)->BulletIsEmpty()) { mHasBullet = true; mLineBox->SetHasBullet(); } WritingMode lineWM = mRootSpan->mWritingMode; PerFrameData* pfd = NewPerFrameData(aFrame); mRootSpan->AppendFrame(pfd); - pfd->SetFlag(PFD_ISBULLET, true); + pfd->mIsBullet = true; if (aMetrics.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { pfd->mAscent = aFrame->GetLogicalBaseline(lineWM); } else { pfd->mAscent = aMetrics.BlockStartAscent(); } // Note: block-coord value will be updated during block-direction alignment pfd->mBounds = LogicalRect(lineWM, aFrame->GetRect(), mContainerWidth); @@ -1508,31 +1527,33 @@ nsLineLayout::VerticalAlignLine() for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { if (pfd->mBlockDirAlign == VALIGN_OTHER) { pfd->mBounds.BStart(lineWM) += baselineBCoord; pfd->mFrame->SetRect(lineWM, pfd->mBounds, mContainerWidth); } } PlaceTopBottomFrames(psd, -mBStartEdge, lineBSize); - // Fill in returned line-box and max-element-width data - mLineBox->SetBounds(lineWM, - psd->mIStart, mBStartEdge, - psd->mICoord - psd->mIStart, lineBSize, - mContainerWidth); + mFinalLineBSize = lineBSize; + if (mGotLineBox) { + // Fill in returned line-box and max-element-width data + mLineBox->SetBounds(lineWM, + psd->mIStart, mBStartEdge, + psd->mICoord - psd->mIStart, lineBSize, + mContainerWidth); - mFinalLineBSize = lineBSize; - mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge); + mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge); #ifdef NOISY_BLOCKDIR_ALIGN - printf( - " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n", - mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM), - mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM), - mFinalLineBSize, mLineBox->GetLogicalAscent()); + printf( + " [line]==> bounds{x,y,w,h}={%d,%d,%d,%d} lh=%d a=%d\n", + mLineBox->GetBounds().IStart(lineWM), mLineBox->GetBounds().BStart(lineWM), + mLineBox->GetBounds().ISize(lineWM), mLineBox->GetBounds().BSize(lineWM), + mFinalLineBSize, mLineBox->GetLogicalAscent()); #endif + } } // Place frames with CSS property vertical-align: top or bottom. void nsLineLayout::PlaceTopBottomFrames(PerSpanData* psd, nscoord aDistanceFromStart, nscoord aLineBSize) { @@ -1709,18 +1730,18 @@ nsLineLayout::VerticalAlignFrames(PerSpa // We shouldn't include any whitespace that collapses, unless we're // preformatted (in which case it shouldn't, but the width=0 test is // perhaps incorrect). This includes whitespace at the beginning of // a line and whitespace preceded (?) by other whitespace. // See bug 134580 and bug 155333. zeroEffectiveSpanBox = true; for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { - if (pfd->GetFlag(PFD_ISTEXTFRAME) && - (pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME) || preMode || + if (pfd->mIsTextFrame && + (pfd->mIsNonWhitespaceTextFrame || preMode || pfd->mBounds.ISize(mRootSpan->mWritingMode) != 0)) { zeroEffectiveSpanBox = false; break; } } } psd->mZeroEffectiveSpanBox = zeroEffectiveSpanBox; @@ -1754,17 +1775,17 @@ nsLineLayout::VerticalAlignFrames(PerSpa CalcLineHeight(spanFrame->GetContent(), spanFrame->StyleContext(), mBlockReflowState->ComputedHeight(), inflation); nscoord contentBSize = spanFramePFD->mBounds.BSize(lineWM) - spanFramePFD->mBorderPadding.BStartEnd(lineWM); // Special-case for a ::first-letter frame, set the line height to // the frame block size if the user has left line-height == normal - if (spanFramePFD->GetFlag(PFD_ISLETTERFRAME) && + if (spanFramePFD->mIsLetterFrame && !spanFrame->GetPrevInFlow() && spanFrame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal) { logicalBSize = spanFramePFD->mBounds.BSize(lineWM); } nscoord leading = logicalBSize - contentBSize; psd->mBStartLeading = leading / 2; psd->mBEndLeading = leading - psd->mBStartLeading; @@ -2064,21 +2085,21 @@ nsLineLayout::VerticalAlignFrames(PerSpa // -- Note #1: With this code enabled and with the fact that we are not // using Em[Ascent|Descent] as nsDimensions for text metrics in // GFX mean that the discussion in bug 13072 cannot hold. // -- Note #2: We still don't want empty-text frames to interfere. // For example in quirks mode, avoiding empty text frames prevents // "tall" lines around elements like <hr> since the rules of <hr> // in quirks.css have pseudo text contents with LF in them. #if 0 - if (!pfd->GetFlag(PFD_ISTEXTFRAME)) { + if (!pfd->mIsTextFrame) { #else // Only consider non empty text frames when line-height=normal - bool canUpdate = !pfd->GetFlag(PFD_ISTEXTFRAME); - if (!canUpdate && pfd->GetFlag(PFD_ISNONWHITESPACETEXTFRAME)) { + bool canUpdate = !pfd->mIsTextFrame; + if (!canUpdate && pfd->mIsNonWhitespaceTextFrame) { canUpdate = frame->StyleText()->mLineHeight.GetUnit() == eStyleUnit_Normal; } if (canUpdate) { #endif nscoord blockStart, blockEnd; if (frameSpan) { // For spans that were are now placing, use their position @@ -2133,17 +2154,18 @@ nsLineLayout::VerticalAlignFrames(PerSpa // is handled by mZeroEffectiveSpanBox // (2) if this line has a bullet // (3) if this is the last line of an LI, DT, or DD element // (The last line before a block also counts, but not before a // BR) (NN4/IE5 quirk) // (1) and (2) above bool applyMinLH = !psd->mZeroEffectiveSpanBox || mHasBullet; - bool isLastLine = (!mLineBox->IsLineWrapped() && !mLineEndsInBR); + bool isLastLine = !mGotLineBox || + (!mLineBox->IsLineWrapped() && !mLineEndsInBR); if (!applyMinLH && isLastLine) { nsIContent* blockContent = mRootSpan->mFrame->mFrame->GetContent(); if (blockContent) { nsIAtom *blockTagAtom = blockContent->Tag(); // (3) above, if the last line of LI, DT, or DD if (blockTagAtom == nsGkAtoms::li || blockTagAtom == nsGkAtoms::dt || blockTagAtom == nsGkAtoms::dd) { @@ -2383,38 +2405,37 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(P // will be run after this method. SlideSpanFrameRect(pfd->mFrame, deltaISize); } } } return true; } } - else if (!pfd->GetFlag(PFD_ISTEXTFRAME) && - !pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) { + else if (!pfd->mIsTextFrame && !pfd->mSkipWhenTrimmingWhitespace) { // If we hit a frame on the end that's not text and not a placeholder, // then there is no trailing whitespace to trim. Stop the search. *aDeltaISize = 0; return true; } - else if (pfd->GetFlag(PFD_ISTEXTFRAME)) { + else if (pfd->mIsTextFrame) { // Call TrimTrailingWhiteSpace even on empty textframes because they // might have a soft hyphen which should now appear, changing the frame's // width nsTextFrame::TrimOutput trimOutput = static_cast<nsTextFrame*>(pfd->mFrame)-> TrimTrailingWhiteSpace(mBlockReflowState->rendContext); #ifdef NOISY_TRIM nsFrame::ListTag(stdout, psd->mFrame->mFrame); printf(": trim of "); nsFrame::ListTag(stdout, pfd->mFrame); printf(" returned %d\n", trimOutput.mDeltaWidth); #endif if (trimOutput.mChanged) { - pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true); + pfd->mRecomputeOverflow = true; } // Delta width not being zero means that // there is trimmed space in the frame. if (trimOutput.mDeltaWidth) { pfd->mBounds.ISize(lineWM) -= trimOutput.mDeltaWidth; // If any trailing space is trimmed, the justification opportunity @@ -2446,17 +2467,17 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(P // that are direct children of the block will be updated // later, however, because the VerticalAlignFrames method // will be run after this method. SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth); } } } - if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) { + if (pfd->mIsNonEmptyTextFrame || trimOutput.mChanged) { // Pass up to caller so they can shrink their span *aDeltaISize = trimOutput.mDeltaWidth; return true; } } pfd = pfd->mPrev; } @@ -2470,92 +2491,154 @@ nsLineLayout::TrimTrailingWhiteSpace() PerSpanData* psd = mRootSpan; nscoord deltaISize; TrimTrailingWhiteSpaceIn(psd, &deltaISize); return 0 != deltaISize; } struct nsLineLayout::JustificationComputationState { + PerFrameData* mFirstParticipant; PerFrameData* mLastParticipant; + // Whether we are going across a boundary of ruby base, i.e. + // entering one, leaving one, or both. + bool mCrossingRubyBaseBoundary; + + JustificationComputationState() + : mFirstParticipant(nullptr) + , mLastParticipant(nullptr) + , mCrossingRubyBaseBoundary(false) { } }; /** - * This function returns the total number of - * expansion opportunities in the given span. + * Compute the justification info of the given span, and store the + * number of inner opportunities into the frame's justification info. + * It returns the number of non-inner opportunities it detects. */ int32_t nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD, JustificationComputationState& aState) { NS_ASSERTION(aPSD, "null arg"); NS_ASSERTION(!aState.mLastParticipant || !aState.mLastParticipant->mSpan, "Last participant shall always be a leaf frame"); - int32_t result = 0; + bool firstChild = true; + int32_t& innerOpportunities = + aPSD->mFrame->mJustificationInfo.mInnerOpportunities; + MOZ_ASSERT(innerOpportunities == 0, + "Justification info should not have been set yet."); + int32_t outerOpportunities = 0; for (PerFrameData* pfd = aPSD->mFirstFrame; pfd; pfd = pfd->mNext) { if (!pfd->ParticipatesInJustification()) { continue; } + bool isRubyBase = pfd->mFrame->GetType() == nsGkAtoms::rubyBaseFrame; + if (isRubyBase) { + aState.mCrossingRubyBaseBoundary = true; + } + + int extraOpportunities = 0; if (pfd->mSpan) { PerSpanData* span = pfd->mSpan; - result += ComputeFrameJustification(span, aState); + extraOpportunities = ComputeFrameJustification(span, aState); + innerOpportunities += pfd->mJustificationInfo.mInnerOpportunities; } else { const auto& info = pfd->mJustificationInfo; - if (pfd->GetFlag(PFD_ISTEXTFRAME)) { - result += info.mInnerOpportunities; + if (pfd->mIsTextFrame) { + innerOpportunities += info.mInnerOpportunities; } PerFrameData* prev = aState.mLastParticipant; - if (prev) { + if (!prev) { + aState.mFirstParticipant = pfd; + } else { auto& assign = pfd->mJustificationAssignment; auto& prevAssign = prev->mJustificationAssignment; const auto& prevInfo = prev->mJustificationInfo; - if (info.mIsStartJustifiable || prevInfo.mIsEndJustifiable) { - result++; - if (!info.mIsStartJustifiable) { + if (info.mIsStartJustifiable || + prevInfo.mIsEndJustifiable || + aState.mCrossingRubyBaseBoundary) { + extraOpportunities = 1; + if (aState.mCrossingRubyBaseBoundary) { + // For ruby alignment with value space-around, there is + // always an expansion opportunity at the boundary of a ruby + // base, and it always generates one gap at each side. If we + // don't do it here, the interaction between text align and + // and ruby align could be strange. + prevAssign.mGapsAtEnd = 1; + assign.mGapsAtStart = 1; + } else if (!info.mIsStartJustifiable) { prevAssign.mGapsAtEnd = 2; assign.mGapsAtStart = 0; } else if (!prevInfo.mIsEndJustifiable) { prevAssign.mGapsAtEnd = 0; assign.mGapsAtStart = 2; } else { prevAssign.mGapsAtEnd = 1; assign.mGapsAtStart = 1; } } } aState.mLastParticipant = pfd; + aState.mCrossingRubyBaseBoundary = isRubyBase; + } + + if (firstChild) { + outerOpportunities = extraOpportunities; + firstChild = false; + } else { + innerOpportunities += extraOpportunities; } } - return result; + return outerOpportunities; } void nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD, nscoord aContainerWidth, nscoord aDeltaICoord, nscoord aDeltaISize) { - MOZ_ASSERT(aPFD->mFrame->GetType() == nsGkAtoms::rubyTextFrame || - aPFD->mFrame->GetType() == nsGkAtoms::rubyTextContainerFrame); + nsIFrame* frame = aPFD->mFrame; + nsIAtom* frameType = frame->GetType(); + MOZ_ASSERT(frameType == nsGkAtoms::rubyTextFrame || + frameType == nsGkAtoms::rubyTextContainerFrame); MOZ_ASSERT(aPFD->mSpan, "rt and rtc should have span."); + PerSpanData* psd = aPFD->mSpan; WritingMode lineWM = mRootSpan->mWritingMode; - WritingMode frameWM = aPFD->mSpan->mWritingMode; - LogicalRect bounds = aPFD->mFrame->GetLogicalRect(aContainerWidth); - bounds = bounds.ConvertTo(lineWM, frameWM, aContainerWidth); + LogicalRect bounds(lineWM, frame->GetRect(), aContainerWidth); bounds.IStart(lineWM) += aDeltaICoord; - bounds.ISize(lineWM) += aDeltaISize; - aPFD->mBounds = bounds.ConvertTo(frameWM, lineWM, aContainerWidth); - aPFD->mFrame->SetRect(frameWM, aPFD->mBounds, aContainerWidth); + + // Check whether this expansion should be counted into the reserved + // isize or not. When it is a ruby text container, and it has some + // children linked to the base, it must not have reserved isize, + // or its children won't align with their bases. Otherwise, this + // expansion should be reserved. There are two cases a ruby text + // container does not have children linked to the base: + // 1. it is a container for span; 2. its children are collapsed. + // See bug 1055674 for the second case. + if (frameType == nsGkAtoms::rubyTextFrame || + // This ruby text container is a span. + (psd->mFirstFrame == psd->mLastFrame && psd->mFirstFrame && + !psd->mFirstFrame->mIsLinkedToBase)) { + nscoord reservedISize = RubyUtils::GetReservedISize(frame); + RubyUtils::SetReservedISize(frame, reservedISize + aDeltaISize); + } else { + // It is a normal ruby text container. Its children will expand + // themselves properly. We only need to expand its own size here. + bounds.ISize(lineWM) += aDeltaISize; + } + aPFD->mBounds = bounds; + aPFD->mFrame->SetRect(lineWM, bounds, aContainerWidth); } /** * This function applies the changes of icoord and isize caused by * justification to annotations of the given frame. * aPFD must be one of the frames in aContainingSpan. */ void @@ -2573,17 +2656,17 @@ nsLineLayout::ApplyLineJustificationToAn // There are two cases where an annotation frame has siblings which // do not attached to a ruby base-level frame: // 1. there's an intra-annotation whitespace which has no intra-base // white-space to pair with; // 2. there are not enough ruby bases to be paired with annotations. // In these cases, their size should not be affected, but we still // need to move them so that they won't overlap other frames. PerFrameData* sibling = pfd->mNext; - while (sibling && !sibling->GetFlag(PFD_ISLINKEDTOBASE)) { + while (sibling && !sibling->mIsLinkedToBase) { AdvanceAnnotationInlineBounds(sibling, containerWidth, aDeltaICoord + aDeltaISize, 0); sibling = sibling->mNext; } pfd = pfd->mNextAnnotation; } } @@ -2592,53 +2675,131 @@ nscoord nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD, JustificationApplicationState& aState) { NS_ASSERTION(aPSD, "null arg"); nscoord deltaICoord = 0; for (PerFrameData* pfd = aPSD->mFirstFrame; pfd != nullptr; pfd = pfd->mNext) { // Don't reposition bullets (and other frames that occur out of X-order?) - if (!pfd->GetFlag(PFD_ISBULLET)) { + if (!pfd->mIsBullet) { nscoord dw = 0; WritingMode lineWM = mRootSpan->mWritingMode; + const auto& assign = pfd->mJustificationAssignment; - pfd->mBounds.IStart(lineWM) += deltaICoord; - - if (true == pfd->GetFlag(PFD_ISTEXTFRAME)) { + if (true == pfd->mIsTextFrame) { if (aState.IsJustifiable()) { // Set corresponding justification gaps here, so that the // text frame knows how it should add gaps at its sides. const auto& info = pfd->mJustificationInfo; - const auto& assign = pfd->mJustificationAssignment; auto textFrame = static_cast<nsTextFrame*>(pfd->mFrame); textFrame->AssignJustificationGaps(assign); dw = aState.Consume(JustificationUtils::CountGaps(info, assign)); } if (dw) { - pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true); + pfd->mRecomputeOverflow = true; } } else { if (nullptr != pfd->mSpan) { dw = ApplyFrameJustification(pfd->mSpan, aState); } } pfd->mBounds.ISize(lineWM) += dw; + if (!pfd->mIsTextFrame && assign.TotalGaps()) { + // It is possible that we assign gaps to non-text frame. + // Apply the gaps as margin around the frame. + deltaICoord += aState.Consume(assign.mGapsAtStart); + dw += aState.Consume(assign.mGapsAtEnd); + } + pfd->mBounds.IStart(lineWM) += deltaICoord; ApplyLineJustificationToAnnotations(pfd, aPSD, deltaICoord, dw); deltaICoord += dw; pfd->mFrame->SetRect(lineWM, pfd->mBounds, ContainerWidthForSpan(aPSD)); } } return deltaICoord; } +/** + * This method expands the given frame by the given reserved isize. + */ +void +nsLineLayout::ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize, + nscoord aContainerWidth) +{ + int32_t opportunities = aFrame->mJustificationInfo.mInnerOpportunities; + // Each expandable ruby box has an gap at each of its sides. For + // rb/rbc, see comment in ComputeFrameJustification; for rt/rtc, + // see comment in this method below. + int32_t gaps = opportunities * 2 + 2; + JustificationApplicationState state(gaps, aReservedISize); + ApplyFrameJustification(aFrame->mSpan, state); + + WritingMode lineWM = mRootSpan->mWritingMode; + aFrame->mBounds.ISize(lineWM) += aReservedISize; + aFrame->mFrame->SetRect(lineWM, aFrame->mBounds, aContainerWidth); +} + +/** + * This method expands the given frame by the reserved inline size. + * It also expands its annotations if they are expandable and have + * reserved isize larger than zero. + */ +void +nsLineLayout::ExpandRubyBoxWithAnnotations(PerFrameData* aFrame, + nscoord aContainerWidth) +{ + nscoord reservedISize = RubyUtils::GetReservedISize(aFrame->mFrame); + if (reservedISize) { + ExpandRubyBox(aFrame, reservedISize, aContainerWidth); + } + + for (PerFrameData* annotation = aFrame->mNextAnnotation; + annotation; annotation = annotation->mNextAnnotation) { + nscoord reservedISize = RubyUtils::GetReservedISize(annotation->mFrame); + if (!reservedISize) { + continue; + } + + MOZ_ASSERT(annotation->mSpan); + JustificationComputationState computeState; + ComputeFrameJustification(annotation->mSpan, computeState); + if (!computeState.mFirstParticipant) { + continue; + } + // Add one gap at each side of this annotation. + computeState.mFirstParticipant->mJustificationAssignment.mGapsAtStart = 1; + computeState.mLastParticipant->mJustificationAssignment.mGapsAtEnd = 1; + ExpandRubyBox(annotation, reservedISize, aContainerWidth); + ExpandInlineRubyBoxes(annotation->mSpan); + } +} + +/** + * This method looks for all expandable ruby box in the given span, and + * calls ExpandRubyBox to expand them in depth-first preorder. + */ +void +nsLineLayout::ExpandInlineRubyBoxes(PerSpanData* aSpan) +{ + nscoord containerWidth = ContainerWidthForSpan(aSpan); + for (PerFrameData* pfd = aSpan->mFirstFrame; pfd; pfd = pfd->mNext) { + if (RubyUtils::IsExpandableRubyBox(pfd->mFrame)) { + ExpandRubyBoxWithAnnotations(pfd, containerWidth); + } + if (pfd->mSpan) { + ExpandInlineRubyBoxes(pfd->mSpan); + } + } +} + // Align inline frames within the line according to the CSS text-align // property. void nsLineLayout::TextAlignLine(nsLineBox* aLine, bool aIsLastLine) { /** * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller @@ -2673,28 +2834,48 @@ nsLineLayout::TextAlignLine(nsLineBox* a if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) { textAlign = NS_STYLE_TEXT_ALIGN_DEFAULT; } } else { textAlign = mStyleText->mTextAlignLast; } } - if ((remainingISize > 0 || textAlignTrue) && - !(mBlockReflowState->frame->IsSVGText())) { + bool isSVG = mBlockReflowState->frame->IsSVGText(); + bool doTextAlign = remainingISize > 0 || textAlignTrue; + int32_t additionalGaps = 0; + if (!isSVG && (mHasRuby || (doTextAlign && + textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY))) { + JustificationComputationState computeState; + ComputeFrameJustification(psd, computeState); + if (mHasRuby && computeState.mFirstParticipant) { + PerFrameData* firstFrame = computeState.mFirstParticipant; + if (firstFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) { + MOZ_ASSERT(!firstFrame->mJustificationAssignment.mGapsAtStart); + firstFrame->mJustificationAssignment.mGapsAtStart = 1; + additionalGaps++; + } + PerFrameData* lastFrame = computeState.mLastParticipant; + if (lastFrame->mFrame->StyleContext()->IsDirectlyInsideRuby()) { + MOZ_ASSERT(!lastFrame->mJustificationAssignment.mGapsAtEnd); + lastFrame->mJustificationAssignment.mGapsAtEnd = 1; + additionalGaps++; + } + } + } + + if (!isSVG && doTextAlign) { switch (textAlign) { case NS_STYLE_TEXT_ALIGN_JUSTIFY: { - JustificationComputationState computeState = { - nullptr // mLastParticipant - }; - int32_t opportunities = ComputeFrameJustification(psd, computeState); + int32_t opportunities = + psd->mFrame->mJustificationInfo.mInnerOpportunities; if (opportunities > 0) { - JustificationApplicationState applyState( - opportunities * 2, remainingISize); + int32_t gaps = opportunities * 2 + additionalGaps; + JustificationApplicationState applyState(gaps, remainingISize); // Apply the justification, and make sure to update our linebox // width to account for it. aLine->ExpandBy(ApplyFrameJustification(psd, applyState), ContainerWidthForSpan(psd)); MOZ_ASSERT(applyState.mGaps.mHandled == applyState.mGaps.mCount, "Unprocessed justification gaps"); @@ -2730,16 +2911,20 @@ nsLineLayout::TextAlignLine(nsLineBox* a case NS_STYLE_TEXT_ALIGN_CENTER: case NS_STYLE_TEXT_ALIGN_MOZ_CENTER: dx = remainingISize / 2; break; } } + if (mHasRuby) { + ExpandInlineRubyBoxes(mRootSpan); + } + if (mPresContext->BidiEnabled() && (!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) { nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame, aLine->GetChildCount(), lineWM, mContainerWidth, psd->mIStart + mTextIndent + dx); if (dx) { aLine->IndentBy(dx, mContainerWidth); @@ -2793,17 +2978,17 @@ nsLineLayout::RelativePositionFrames(Per overflowAreas.ScrollableOverflow() = overflowAreas.VisualOverflow(); } for (PerFrameData* pfd = psd->mFirstFrame; pfd; pfd = pfd->mNext) { nsIFrame* frame = pfd->mFrame; nsPoint origin = frame->GetPosition(); // Adjust the origin of the frame - if (pfd->GetFlag(PFD_RELATIVEPOS)) { + if (pfd->mRelativePos) { //XXX temporary until ApplyRelativePositioning can handle logical offsets nsMargin physicalOffsets = pfd->mOffsets.GetPhysicalMargin(pfd->mFrame->GetWritingMode()); // right and bottom are handled by // nsHTMLReflowState::ComputeRelativeOffsets nsHTMLReflowState::ApplyRelativePositioning(pfd->mFrame, physicalOffsets, &origin); @@ -2824,22 +3009,22 @@ nsLineLayout::RelativePositionFrames(Per // <b>x</b> and <b>y</b> which were computed above. nsOverflowAreas r; if (pfd->mSpan) { // Compute a new combined area for the child span before // aggregating it into our combined area. RelativePositionFrames(pfd->mSpan, r); } else { r = pfd->mOverflowAreas; - if (pfd->GetFlag(PFD_ISTEXTFRAME)) { + if (pfd->mIsTextFrame) { // We need to recompute overflow areas in two cases: // (1) When PFD_RECOMPUTEOVERFLOW is set due to trimming // (2) When there are text decorations, since we can't recompute the // overflow area until Reflow and VerticalAlignLine have finished - if (pfd->GetFlag(PFD_RECOMPUTEOVERFLOW) || + if (pfd->mRecomputeOverflow || frame->StyleContext()->HasTextDecorationLines()) { nsTextFrame* f = static_cast<nsTextFrame*>(frame); r = f->RecomputeOverflow(*mBlockReflowState); } frame->FinishAndStoreOverflow(r, frame->GetSize()); } // If we have something that's not an inline but with a complex frame
--- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -111,16 +111,25 @@ public: PushFrame(aFrame); } /** * Place frames in the block direction (CSS property vertical-align) */ void VerticalAlignLine(); + // Get the final size of the line, in the block direction. + // Do not call this until after we've called VerticalAlignLine. + nscoord GetFinalLineBSize() const + { + NS_ASSERTION(mFinalLineBSize != nscoord_MIN, + "VerticalAlignLine should have been called before"); + return mFinalLineBSize; + } + bool TrimTrailingWhiteSpace(); /** * Place frames in the inline direction (CSS property text-align). */ void TextAlignLine(nsLineBox* aLine, bool aIsLastLine); /** @@ -432,57 +441,37 @@ protected: nsOverflowAreas mOverflowAreas; // From reflow-state mozilla::LogicalMargin mMargin; // in *line* writing mode mozilla::LogicalMargin mBorderPadding; // in *line* writing mode mozilla::LogicalMargin mOffsets; // in *frame* writing mode // state for text justification + // Note that, although all frames would have correct inner + // opportunities computed after ComputeFrameJustification, start + // and end justifiable info are not reliable for non-text frames. mozilla::JustificationInfo mJustificationInfo; mozilla::JustificationAssignment mJustificationAssignment; -// PerFrameData flags -#define PFD_RELATIVEPOS 0x00000001 -#define PFD_ISTEXTFRAME 0x00000002 -#define PFD_ISNONEMPTYTEXTFRAME 0x00000004 -#define PFD_ISNONWHITESPACETEXTFRAME 0x00000008 -#define PFD_ISLETTERFRAME 0x00000010 -#define PFD_RECOMPUTEOVERFLOW 0x00000020 -#define PFD_ISBULLET 0x00000040 -#define PFD_SKIPWHENTRIMMINGWHITESPACE 0x00000080 -#define PFD_ISEMPTY 0x00000100 -#define PFD_ISLINKEDTOBASE 0x00000200 -#define PFD_LASTFLAG PFD_ISLINKEDTOBASE + // PerFrameData flags + bool mRelativePos : 1; + bool mIsTextFrame : 1; + bool mIsNonEmptyTextFrame : 1; + bool mIsNonWhitespaceTextFrame : 1; + bool mIsLetterFrame : 1; + bool mRecomputeOverflow : 1; + bool mIsBullet : 1; + bool mSkipWhenTrimmingWhitespace : 1; + bool mIsEmpty : 1; + bool mIsLinkedToBase : 1; // Other state we use - uint16_t mFlags; uint8_t mBlockDirAlign; - static_assert(PFD_LASTFLAG <= UINT16_MAX, - "Flag value exceeds the length of flags variable."); - - void SetFlag(uint32_t aFlag, bool aValue) - { - NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); - if (aValue) { // set flag - mFlags |= aFlag; - } - else { // unset flag - mFlags &= ~aFlag; - } - } - - bool GetFlag(uint32_t aFlag) const - { - NS_ASSERTION(aFlag<=PFD_LASTFLAG, "bad flag"); - return !!(mFlags & aFlag); - } - - PerFrameData* Last() { PerFrameData* pfd = this; while (pfd->mNext) { pfd = pfd->mNext; } return pfd; } @@ -494,17 +483,17 @@ protected: bool IsEndJustifiable() const { return mJustificationInfo.mIsEndJustifiable; } bool ParticipatesInJustification() const { // Skip bullets and empty frames - return !GetFlag(PFD_ISBULLET) && !GetFlag(PFD_ISEMPTY); + return !mIsBullet && !mIsEmpty; } }; PerFrameData* mFrameFreeList; struct PerSpanData { union { PerSpanData* mParent; PerSpanData* mNextFreeSpan; @@ -596,16 +585,17 @@ protected: bool mLineEndsInBR : 1; bool mNeedBackup : 1; bool mInFirstLine : 1; bool mGotLineBox : 1; bool mInFirstLetter : 1; bool mHasBullet : 1; bool mDirtyNextLine : 1; bool mLineAtStart : 1; + bool mHasRuby : 1; int32_t mSpanDepth; #ifdef DEBUG int32_t mSpansAllocated, mSpansFreed; int32_t mFramesAllocated, mFramesFreed; #endif PLArenaPool mArena; // Per span and per frame data, 4 byte aligned @@ -680,16 +670,24 @@ protected: nscoord aDeltaICoord, nscoord aDeltaISize); // Apply justification. The return value is the amount by which the width of // the span corresponding to aPSD got increased due to justification. nscoord ApplyFrameJustification( PerSpanData* aPSD, mozilla::JustificationApplicationState& aState); + void ExpandRubyBox(PerFrameData* aFrame, nscoord aReservedISize, + nscoord aContainerWidth); + + void ExpandRubyBoxWithAnnotations(PerFrameData* aFrame, + nscoord aContainerWidth); + + void ExpandInlineRubyBoxes(PerSpanData* aSpan); + void AttachFrameToBaseLineLayout(PerFrameData* aFrame); #ifdef DEBUG void DumpPerSpanData(PerSpanData* psd, int32_t aIndent); #endif }; #endif /* nsLineLayout_h___ */
--- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -8,16 +8,17 @@ #include "nsRubyBaseContainerFrame.h" #include "nsContentUtils.h" #include "nsLineLayout.h" #include "nsPresContext.h" #include "nsStyleContext.h" #include "nsStyleStructInlines.h" #include "WritingModes.h" +#include "RubyUtils.h" using namespace mozilla; //---------------------------------------------------------------------- // Frame class boilerplate // ======================= @@ -371,49 +372,66 @@ nsRubyBaseContainerFrame::Reflow(nsPresC // reflowed, or be not reflowed at all. MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || NS_FRAME_IS_COMPLETE(aStatus) || mSpanContainers.IsEmpty()); if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && NS_FRAME_IS_COMPLETE(aStatus) && !mSpanContainers.IsEmpty()) { // Reflow spans nscoord spanISize = ReflowSpans(aPresContext, aReflowState, spanReflowStates); - if (isize < spanISize) { - nscoord delta = spanISize - isize; - if (allowLineBreak && ShouldBreakBefore(aReflowState, delta)) { - aStatus = NS_INLINE_LINE_BREAK_BEFORE(); - } else { - aReflowState.mLineLayout->AdvanceICoord(delta); - isize = spanISize; - } + nscoord deltaISize = spanISize - isize; + if (deltaISize <= 0) { + RubyUtils::ClearReservedISize(this); + } else if (allowLineBreak && ShouldBreakBefore(aReflowState, deltaISize)) { + aStatus = NS_INLINE_LINE_BREAK_BEFORE(); + } else { + RubyUtils::SetReservedISize(this, deltaISize); + aReflowState.mLineLayout->AdvanceICoord(deltaISize); + isize = spanISize; } + } // When there are spans, ReflowPairs and ReflowOnePair won't // record any optional break position. We have to record one // at the end of this segment. if (!NS_INLINE_IS_BREAK(aStatus) && allowLineBreak && aReflowState.mLineLayout->NotifyOptionalBreakPosition( this, INT32_MAX, startEdge + isize <= aReflowState.AvailableISize(), gfxBreakPriority::eNormalBreak)) { aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus); } - } DebugOnly<nscoord> lineSpanSize = aReflowState.mLineLayout->EndSpan(this); // When there are no frames inside the ruby base container, EndSpan // will return 0. However, in this case, the actual width of the // container could be non-zero because of non-empty ruby annotations. MOZ_ASSERT(NS_INLINE_IS_BREAK_BEFORE(aStatus) || isize == lineSpanSize || mFrames.IsEmpty()); for (uint32_t i = 0; i < totalCount; i++) { // It happens before the ruby text container is reflowed, and that // when it is reflowed, it will just use this size. nsRubyTextContainerFrame* textContainer = i < rtcCount ? mTextContainers[i] : mSpanContainers[i - rtcCount]; - textContainer->SetISize(isize); - lineLayouts[i]->EndLineReflow(); + nsLineLayout* lineLayout = lineLayouts[i].get(); + + RubyUtils::ClearReservedISize(textContainer); + nscoord rtcISize = lineLayout->GetCurrentICoord(); + // Only span containers and containers with collapsed annotations + // need reserving isize. For normal ruby text containers, their + // children will be expanded properly. We only need to expand their + // own size. + if (i < rtcCount) { + rtcISize = isize; + } else if (isize > rtcISize) { + RubyUtils::SetReservedISize(textContainer, isize - rtcISize); + } + + lineLayout->VerticalAlignLine(); + LogicalSize lineSize(lineWM, isize, lineLayout->GetFinalLineBSize()); + textContainer->SetLineSize(lineSize); + lineLayout->EndLineReflow(); } aDesiredSize.ISize(lineWM) = isize; nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, borderPadding, lineWM, frameWM); } /** @@ -563,16 +581,17 @@ nsRubyBaseContainerFrame::ReflowOnePair( if (annotationText.Equals(baseText)) { textFrame->AddStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE); } else { textFrame->RemoveStateBits(NS_RUBY_TEXT_FRAME_AUTOHIDE); } nsReflowStatus reflowStatus; nsHTMLReflowMetrics metrics(*aReflowStates[i]); + RubyUtils::ClearReservedISize(textFrame); bool pushedFrame; aReflowStates[i]->mLineLayout->ReflowFrame(textFrame, reflowStatus, &metrics, pushedFrame); MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame, "Any line break inside ruby box should has been suppressed"); pairISize = std::max(pairISize, metrics.ISize(lineWM)); } @@ -585,33 +604,46 @@ nsRubyBaseContainerFrame::ReflowOnePair( return 0; } // Reflow the base frame if (aBaseFrame) { MOZ_ASSERT(aBaseFrame->GetType() == nsGkAtoms::rubyBaseFrame); nsReflowStatus reflowStatus; nsHTMLReflowMetrics metrics(aReflowState); + RubyUtils::ClearReservedISize(aBaseFrame); bool pushedFrame; aReflowState.mLineLayout->ReflowFrame(aBaseFrame, reflowStatus, &metrics, pushedFrame); MOZ_ASSERT(!NS_INLINE_IS_BREAK(reflowStatus) && !pushedFrame, "Any line break inside ruby box should has been suppressed"); pairISize = std::max(pairISize, metrics.ISize(lineWM)); } // Align all the line layout to the new coordinate. nscoord icoord = istart + pairISize; - aReflowState.mLineLayout->AdvanceICoord( - icoord - aReflowState.mLineLayout->GetCurrentICoord()); + nscoord deltaISize = icoord - aReflowState.mLineLayout->GetCurrentICoord(); + if (deltaISize > 0) { + aReflowState.mLineLayout->AdvanceICoord(deltaISize); + if (aBaseFrame) { + RubyUtils::SetReservedISize(aBaseFrame, deltaISize); + } + } for (uint32_t i = 0; i < rtcCount; i++) { nsLineLayout* lineLayout = aReflowStates[i]->mLineLayout; - lineLayout->AdvanceICoord(icoord - lineLayout->GetCurrentICoord()); - if (aBaseFrame && aTextFrames[i]) { + nsIFrame* textFrame = aTextFrames[i]; + nscoord deltaISize = icoord - lineLayout->GetCurrentICoord(); + if (deltaISize > 0) { + lineLayout->AdvanceICoord(deltaISize); + if (textFrame) { + RubyUtils::SetReservedISize(textFrame, deltaISize); + } + } + if (aBaseFrame && textFrame) { lineLayout->AttachLastFrameToBaseLineLayout(); } } mPairCount++; if (aAllowLineBreak && aReflowState.mLineLayout->NotifyOptionalBreakPosition( this, mPairCount, icoord <= aReflowState.AvailableISize(),
--- a/layout/generic/nsRubyFrame.cpp +++ b/layout/generic/nsRubyFrame.cpp @@ -266,16 +266,33 @@ nsRubyFrame::Reflow(nsPresContext* aPres // We never handle overflow in ruby. MOZ_ASSERT(!NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)); aDesiredSize.ISize(lineWM) = aReflowState.mLineLayout->EndSpan(this); nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, borderPadding, lineWM, frameWM); } +#ifdef DEBUG +static void +SanityCheckRubyPosition(int8_t aRubyPosition) +{ + uint8_t horizontalPosition = aRubyPosition & + (NS_STYLE_RUBY_POSITION_LEFT | NS_STYLE_RUBY_POSITION_RIGHT); + MOZ_ASSERT(horizontalPosition == NS_STYLE_RUBY_POSITION_LEFT || + horizontalPosition == NS_STYLE_RUBY_POSITION_RIGHT); + uint8_t verticalPosition = aRubyPosition & + (NS_STYLE_RUBY_POSITION_OVER | NS_STYLE_RUBY_POSITION_UNDER | + NS_STYLE_RUBY_POSITION_INTER_CHARACTER); + MOZ_ASSERT(verticalPosition == NS_STYLE_RUBY_POSITION_OVER || + verticalPosition == NS_STYLE_RUBY_POSITION_UNDER || + verticalPosition == NS_STYLE_RUBY_POSITION_INTER_CHARACTER); +} +#endif + void nsRubyFrame::ReflowSegment(nsPresContext* aPresContext, const nsHTMLReflowState& aReflowState, nsRubyBaseContainerFrame* aBaseContainer, nsReflowStatus& aStatus) { AutoSetTextContainers holder(aBaseContainer); WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); @@ -351,16 +368,26 @@ nsRubyFrame::ReflowSegment(nsPresContext nsIFrame* nextRTC = textContainers[i]->GetNextInFlow(); if (nextRTC) { nextRTC->GetParent()->DeleteNextInFlowChild(nextRTC, true); } } } nsRect baseRect = aBaseContainer->GetRect(); + // We need to position our rtc frames on one side or the other of the + // base container's rect, using a coordinate space that's relative to + // the ruby frame. Right now, the base container's rect's block-axis + // position is relative to the block container frame containing the + // lines, so we use 0 instead. (i.e. we assume that the base container + // is adjacent to the ruby frame's block-start edge.) + // XXX We may need to add border/padding here. See bug 1055667. + (lineWM.IsVertical() ? baseRect.x : baseRect.y) = 0; + // The rect for offsets of text containers. + nsRect offsetRect = baseRect; for (uint32_t i = 0; i < rtcCount; i++) { nsRubyTextContainerFrame* textContainer = textContainers[i]; nsReflowStatus textReflowStatus; nsHTMLReflowMetrics textMetrics(aReflowState); nsHTMLReflowState textReflowState(aPresContext, aReflowState, textContainer, availSize); // FIXME We probably shouldn't be using the same nsLineLayout for // the text containers. But it should be fine now as we are @@ -371,24 +398,47 @@ nsRubyFrame::ReflowSegment(nsPresContext textReflowState, textReflowStatus); // Ruby text containers always return NS_FRAME_COMPLETE even when // they have continuations, because the breaking has already been // handled when reflowing the base containers. NS_ASSERTION(textReflowStatus == NS_FRAME_COMPLETE, "Ruby text container must not break itself inside"); textContainer->SetSize(LogicalSize(lineWM, textMetrics.ISize(lineWM), textMetrics.BSize(lineWM))); + nscoord x, y; nscoord bsize = textMetrics.BSize(lineWM); + uint8_t rubyPosition = textContainer->StyleText()->mRubyPosition; +#ifdef DEBUG + SanityCheckRubyPosition(rubyPosition); +#endif if (lineWM.IsVertical()) { - x = lineWM.IsVerticalLR() ? -bsize : baseRect.XMost(); - y = baseRect.Y(); + // writing-mode is vertical, so bsize is the annotation's *width* + if (rubyPosition & NS_STYLE_RUBY_POSITION_LEFT) { + x = offsetRect.X() - bsize; + offsetRect.SetLeftEdge(x); + } else { + x = offsetRect.XMost(); + offsetRect.SetRightEdge(x + bsize); + } + y = offsetRect.Y(); } else { - x = baseRect.X(); - y = -bsize; + // writing-mode is horizontal, so bsize is the annotation's *height* + x = offsetRect.X(); + if (rubyPosition & NS_STYLE_RUBY_POSITION_OVER) { + y = offsetRect.Y() - bsize; + offsetRect.SetTopEdge(y); + } else if (rubyPosition & NS_STYLE_RUBY_POSITION_UNDER) { + y = offsetRect.YMost(); + offsetRect.SetBottomEdge(y + bsize); + } else { + // XXX inter-character support in bug 1055672 + MOZ_ASSERT_UNREACHABLE("Unsupported ruby-position"); + y = offsetRect.Y(); + } } FinishReflowChild(textContainer, aPresContext, textMetrics, &textReflowState, x, y, 0); } } nsRubyBaseContainerFrame* nsRubyFrame::PullOneSegment(ContinuationTraversingState& aState)
--- a/layout/generic/nsRubyTextContainerFrame.cpp +++ b/layout/generic/nsRubyTextContainerFrame.cpp @@ -66,25 +66,19 @@ nsRubyTextContainerFrame::Reflow(nsPresC nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { DO_GLOBAL_REFLOW_COUNT("nsRubyTextContainerFrame"); DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); // All rt children have already been reflowed. All we need to do is - // to report complete and return the desired size. + // to report complete and return the desired size provided by the + // ruby base container. // Although a ruby text container may have continuations, returning // NS_FRAME_COMPLETE here is still safe, since its parent, ruby frame, // ignores the status, and continuations of the ruby base container // will take care of our continuations. aStatus = NS_FRAME_COMPLETE; WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); - WritingMode frameWM = aReflowState.GetWritingMode(); - LogicalMargin borderPadding = aReflowState.ComputedLogicalBorderPadding(); - - // ISize is provided by the ruby base container - // during reflow of that container. - aDesiredSize.ISize(lineWM) = mISize; - nsLayoutUtils::SetBSizeFromFontMetrics(this, aDesiredSize, aReflowState, - borderPadding, lineWM, frameWM); + aDesiredSize.SetSize(lineWM, mLineSize); }
--- a/layout/generic/nsRubyTextContainerFrame.h +++ b/layout/generic/nsRubyTextContainerFrame.h @@ -42,20 +42,21 @@ public: virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE; #endif protected: friend nsContainerFrame* NS_NewRubyTextContainerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); explicit nsRubyTextContainerFrame(nsStyleContext* aContext) - : nsRubyTextContainerFrameSuper(aContext) {} + : nsRubyTextContainerFrameSuper(aContext) + , mLineSize(mozilla::WritingMode(aContext)) {} friend class nsRubyBaseContainerFrame; - void SetISize(nscoord aISize) { mISize = aISize; } + void SetLineSize(const mozilla::LogicalSize& aSize) { mLineSize = aSize; } - // The intended dimensions of the ruby text container. These are modified - // whenever a ruby text box is reflowed and used when the ruby text container - // is reflowed. - nscoord mISize; + // The intended dimensions of the ruby text container. It is set by + // the corresponding ruby base container when the segment is reflowed, + // and used when the ruby text container is reflowed by its parent. + mozilla::LogicalSize mLineSize; }; #endif /* nsRubyTextContainerFrame_h___ */
--- a/layout/generic/nsRubyTextFrame.cpp +++ b/layout/generic/nsRubyTextFrame.cpp @@ -85,12 +85,15 @@ nsRubyTextFrame::Reflow(nsPresContext* a // propagated to the ancestors, then it won't be displayed even if // the content is no longer the same, until next reflow triggered by // some other change. In general, we always reflow all the frames we // created. There might be other problems if we don't do that. nsRubyTextFrameSuper::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); if (GetStateBits() & NS_RUBY_TEXT_FRAME_AUTOHIDE) { - aDesiredSize.ClearSize(); + // Reset the ISize. The BSize is not changed so that it won't + // affect vertical positioning in unexpected way. + WritingMode lineWM = aReflowState.mLineLayout->GetWritingMode(); + aDesiredSize.ISize(lineWM) = 0; aDesiredSize.SetOverflowAreasToDesiredBounds(); } }
--- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -1856,20 +1856,20 @@ BuildTextRunsScanner::BuildTextRunForFra } userData->mMappedFlowCount = mMappedFlows.Length(); userData->mLastFlowIndex = 0; uint32_t currentTransformedTextOffset = 0; uint32_t nextBreakIndex = 0; nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex); + bool isSVG = mLineContainer->IsSVGText(); bool enabledJustification = mLineContainer && (mLineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY || - mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY) && - !mLineContainer->IsSVGText(); + mLineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY); // for word-break style switch (mLineContainer->StyleText()->mWordBreak) { case NS_STYLE_WORDBREAK_BREAK_ALL: mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_BreakAll); break; case NS_STYLE_WORDBREAK_KEEP_ALL: mLineBreaker.SetWordBreak(nsILineBreaker::kWordBreak_KeepAll); @@ -1891,17 +1891,18 @@ BuildTextRunsScanner::BuildTextRunForFra textStyle = f->StyleText(); if (NS_STYLE_TEXT_TRANSFORM_NONE != textStyle->mTextTransform) { anyTextTransformStyle = true; } textFlags |= GetSpacingFlags(LetterSpacing(f)); textFlags |= GetSpacingFlags(WordSpacing(f)); nsTextFrameUtils::CompressionMode compression = CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace]; - if (enabledJustification && !textStyle->WhiteSpaceIsSignificant()) { + if ((enabledJustification || f->StyleContext()->IsDirectlyInsideRuby()) && + !textStyle->WhiteSpaceIsSignificant() && !isSVG) { textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING; } fontStyle = f->StyleFont(); nsIFrame* parent = mLineContainer->GetParent(); if (NS_MATHML_MATHVARIANT_NONE != fontStyle->mMathVariant) { if (NS_MATHML_MATHVARIANT_NORMAL != fontStyle->mMathVariant) { anyMathMLStyling = true; } @@ -2755,16 +2756,22 @@ static int32_t FindChar(const nsTextFrag return (static_cast<const char*>(p) - str) + aOffset; } } return -1; } static bool IsChineseOrJapanese(nsIFrame* aFrame) { + if (aFrame->StyleContext()->IsDirectlyInsideRuby()) { + // Always treat ruby as CJ language so that those characters can + // be expanded properly even when surrounded by other language. + return true; + } + nsIAtom* language = aFrame->StyleFont()->mLanguage; if (!language) { return false; } const char16_t *lang = language->GetUTF16String(); return (!nsCRT::strncmp(lang, MOZ_UTF16("ja"), 2) || !nsCRT::strncmp(lang, MOZ_UTF16("zh"), 2)) && (language->GetLength() == 2 || lang[2] == '-'); @@ -8497,17 +8504,18 @@ nsTextFrame::ReflowText(nsLineLayout& aL } } else if (cachedNewlineOffset) { mContent->DeleteProperty(nsGkAtoms::newline); } // Compute space and letter counts for justification, if required if (!textStyle->WhiteSpaceIsSignificant() && (lineContainer->StyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY || - lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY) && + lineContainer->StyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY || + StyleContext()->IsDirectlyInsideRuby()) && !lineContainer->IsSVGText()) { AddStateBits(TEXT_JUSTIFICATION_ENABLED); provider.ComputeJustification(offset, charsFit); aLineLayout.SetJustificationInfo(provider.GetJustificationInfo()); } SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
--- a/layout/reftests/css-ruby/reftest.list +++ b/layout/reftests/css-ruby/reftest.list @@ -1,13 +1,13 @@ default-preferences pref(layout.css.ruby.enabled,true) == autohiding-1.html autohiding-1-ref.html == autohiding-2.html autohiding-2-ref.html -== autohiding-3.html autohiding-3-ref.html +fails == autohiding-3.html autohiding-3-ref.html # bug 1107701 == box-generation-1.html box-generation-1-ref.html == box-generation-2.html box-generation-2-ref.html == box-generation-3.html box-generation-3-ref.html == box-generation-4.html box-generation-4-ref.html == box-generation-5.html box-generation-5-ref.html == dynamic-insertion-1.html dynamic-insertion-1-ref.html == dynamic-insertion-2.html dynamic-insertion-2-ref.html == dynamic-insertion-3.html dynamic-insertion-3-ref.html @@ -17,10 +17,13 @@ fuzzy-if(winWidget,28,1) == dynamic-remo == float-handling.html float-handling-ref.html == inlinize-blocks-1.html inlinize-blocks-1-ref.html == inlinize-blocks-2.html inlinize-blocks-2-ref.html == inlinize-blocks-3.html inlinize-blocks-3-ref.html == inlinize-blocks-4.html inlinize-blocks-4-ref.html == inlinize-blocks-5.html inlinize-blocks-5-ref.html == ruby-whitespace-1.html ruby-whitespace-1-ref.html == ruby-whitespace-2.html ruby-whitespace-2-ref.html +== ruby-position-horizontal.html ruby-position-horizontal-ref.html +pref(layout.css.vertical-text.enabled,true) fails == ruby-position-vertical-lr.html ruby-position-vertical-lr-ref.html # bug 1112474 +pref(layout.css.vertical-text.enabled,true) fails == ruby-position-vertical-rl.html ruby-position-vertical-rl-ref.html # bug 1112474 != ruby-reflow-1-opaqueruby.html ruby-reflow-1-noruby.html == ruby-reflow-1-transparentruby.html ruby-reflow-1-noruby.html
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-horizontal-ref.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + line-height: normal; + } + .annotation, .annotation > div { + position: absolute; + } + </style> +</head> +<body> + <div style="height: 8em; line-height: 8em;"> + <div style="display: inline-block; line-height: normal;"> + <div class="annotation"> + <div style="top: -100%;">over##</div> + <div style="top: 100%;">under#</div> + <div style="top: -200%;">over2#</div> + <div style="top: 200%;">under2</div> + <!-- to give container a nonzero size for + percent values to resolve against --> + </div> + base## + </div> + </div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-horizontal.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + /* use a large line-height here to avoid additional leadings */ + line-height: 8em; + } + rtc, rt { + font-size: 100% !important; + line-height: normal !important; + } + </style> +</head> +<body> + <ruby> + <rb>base##</rb> + <rtc style="ruby-position: over left"><rt>over##</rt></rtc> + <rtc style="ruby-position: under left"><rt>under#</rt></rtc> + <rtc style="ruby-position: over left"><rt>over2#</rt></rtc> + <rtc style="ruby-position: under left"><rt>under2</rt></rtc> + </ruby> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-vertical-lr-ref.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + line-height: normal; + writing-mode: vertical-lr; + } + .annotation, .annotation > div { + position: absolute; + } + </style> +</head> +<body> + <div style="width: 8em; line-height: 8em;"> + <div style="display: inline-block; line-height: normal;"> + <div class="annotation"> + <div style="right: 100%;">left##</div> + <div style="right: -100%;">right#</div> + <div style="right: 200%;">left2#</div> + <div style="right: -200%;">right2</div> + <!-- to give container a nonzero size for + percent values to resolve against --> + </div> + base## + </div> + </div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-vertical-lr.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + /* use a large line-height here to avoid additional leadings */ + line-height: 8em; + writing-mode: vertical-lr; + } + rtc, rt { + font-size: 100% !important; + line-height: normal !important; + } + </style> +</head> +<body> + <ruby> + <rb>base##</rb> + <rtc style="ruby-position: over left"><rt>left##</rt></rtc> + <rtc style="ruby-position: over right"><rt>right#</rt></rtc> + <rtc style="ruby-position: over left"><rt>left2#</rt></rtc> + <rtc style="ruby-position: over right"><rt>right2</rt></rtc> + </ruby> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-vertical-rl-ref.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + line-height: normal; + writing-mode: vertical-rl; + } + .annotation, .annotation > div { + position: absolute; + } + </style> +</head> +<body> + <div style="width: 8em; line-height: 8em;"> + <div style="display: inline-block; line-height: normal;"> + <div class="annotation"> + <div style="right: 100%;">left##</div> + <div style="right: -100%;">right#</div> + <div style="right: 200%;">left2#</div> + <div style="right: -200%;">right2</div> + <!-- to give container a nonzero size for + percent values to resolve against --> + </div> + base## + </div> + </div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/css-ruby/ruby-position-vertical-rl.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <title>Bug 1055665 - Test for ruby-position</title> + <link rel="stylesheet" href="common.css"> + <style> + body { + font-family: monospace; + /* use a large line-height here to avoid additional leadings */ + line-height: 8em; + writing-mode: vertical-rl; + } + rtc, rt { + font-size: 100% !important; + line-height: normal !important; + } + </style> +</head> +<body> + <ruby> + <rb>base##</rb> + <rtc style="ruby-position: over left"><rt>left##</rt></rtc> + <rtc style="ruby-position: over right"><rt>right#</rt></rtc> + <rtc style="ruby-position: over left"><rt>left2#</rt></rtc> + <rtc style="ruby-position: over right"><rt>right2</rt></rtc> + </ruby> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/floats/1114329-ref.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + p { border: 1px solid black; } + .layout-main-page-content { width: 75%; float: right; text-align:right; } + .layout-right-column { width: 25%; float: left; } + </style> + </head> + <body> + <div> + <div class="layout-main-page-content"> + <img src="foo"> + <p dir="rtl" id="p_10">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p> + <p dir="rtl" id="p_20">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p> + </div> + </div> + <div class="layout-right-column" dir="rtl"> + hoge hoge hoge hoge hoge hoge hoge hoge hoge hoge + <img id="image" style="height: 280px; overflow: hidden;" src="foo" width="100%"> + </div> + </body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/floats/1114329.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html class="reftest-wait"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + p { border: 1px solid black; } + .layout-main-page-content { width: 75%; float: right;} + .layout-right-column { width: 25%; float: left; } + </style> + <script type="text/javascript"> + function do_test() { + document.getElementById("image").removeAttribute("hidden"); + document.documentElement.removeAttribute("class"); + }; + </script> + </head> + <body dir="rtl" onload="do_test()"> + <div> + <div class="layout-main-page-content"> + <img src="foo"> + <p dir="rtl" id="p_10">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p> + <p dir="rtl" id="p_20">0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789 0123456789</p> + </div> + </div> + <div class="layout-right-column"> + hoge hoge hoge hoge hoge hoge hoge hoge hoge hoge + <img id="image" hidden="true" style="height: 280px; overflow: hidden;" src="foo" width="100%"> + </div> + </body> +</html>
--- a/layout/reftests/floats/reftest.list +++ b/layout/reftests/floats/reftest.list @@ -13,8 +13,9 @@ fails == zero-height-float.html zero-hei fails == 345369-1.html 345369-1-ref.html fails == 345369-2.html 345369-2-ref.html == 345369-3.html 345369-3-ref.html == 345369-4.html 345369-4-ref.html == 345369-5.html 345369-5-ref.html == 429974-1.html 429974-1-ref.html == 546048-1.html 546048-1-ref.html == 775350-1.html 775350-1-ref.html +== 1114329.html 1114329-ref.html
--- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -305,16 +305,17 @@ CSS_KEY(initial, initial) CSS_KEY(inline, inline) CSS_KEY(inline-axis, inline_axis) CSS_KEY(inline-block, inline_block) CSS_KEY(inline-flex, inline_flex) CSS_KEY(inline-grid, inline_grid) CSS_KEY(inline-table, inline_table) CSS_KEY(inset, inset) CSS_KEY(inside, inside) +// CSS_KEY(inter-character, inter_character) // TODO see bug 1055672 CSS_KEY(interpolatematrix, interpolatematrix) CSS_KEY(isolate, isolate) CSS_KEY(invert, invert) CSS_KEY(italic, italic) CSS_KEY(japanese-formal, japanese_formal) CSS_KEY(japanese-informal, japanese_informal) CSS_KEY(jis78, jis78) CSS_KEY(jis83, jis83) @@ -388,16 +389,17 @@ CSS_KEY(nwse-resize, nwse_resize) CSS_KEY(oblique, oblique) CSS_KEY(oldstyle-nums, oldstyle_nums) CSS_KEY(opacity, opacity) CSS_KEY(open-quote, open_quote) CSS_KEY(ordinal, ordinal) CSS_KEY(ornaments, ornaments) CSS_KEY(outset, outset) CSS_KEY(outside, outside) +CSS_KEY(over, over) CSS_KEY(overlay, overlay) CSS_KEY(overline, overline) CSS_KEY(padding-box, padding_box) CSS_KEY(painted, painted) CSS_KEY(pan-x, pan_x) CSS_KEY(pan-y, pan_y) CSS_KEY(paused, paused) CSS_KEY(pc, pc) @@ -548,16 +550,17 @@ CSS_KEY(translate3d, translate3d) CSS_KEY(translatex, translatex) CSS_KEY(translatey, translatey) CSS_KEY(translatez, translatez) CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only CSS_KEY(tri-state, tri_state) CSS_KEY(true, true) CSS_KEY(ultra-condensed, ultra_condensed) CSS_KEY(ultra-expanded, ultra_expanded) +CSS_KEY(under, under) CSS_KEY(underline, underline) CSS_KEY(unicase, unicase) CSS_KEY(unset, unset) CSS_KEY(uppercase, uppercase) CSS_KEY(upright, upright) CSS_KEY(vertical, vertical) CSS_KEY(vertical-lr, vertical_lr) CSS_KEY(vertical-rl, vertical_rl)
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -790,16 +790,17 @@ protected: bool ParseMarks(nsCSSValue& aValue); bool ParseClipPath(); bool ParseTransform(bool aIsPrefixed); bool ParseObjectPosition(); bool ParseOutline(); bool ParseOverflow(); bool ParsePadding(); bool ParseQuotes(); + bool ParseRubyPosition(nsCSSValue& aValue); bool ParseSize(); bool ParseTextAlign(nsCSSValue& aValue, const KTableValue aTable[]); bool ParseTextAlign(nsCSSValue& aValue); bool ParseTextAlignLast(nsCSSValue& aValue); bool ParseTextDecoration(); bool ParseTextDecorationLine(nsCSSValue& aValue); bool ParseTextCombineUpright(nsCSSValue& aValue); @@ -10004,16 +10005,18 @@ CSSParserImpl::ParseSingleValueProperty( case eCSSProperty_font_weight: return ParseFontWeight(aValue); case eCSSProperty_image_orientation: return ParseImageOrientation(aValue); case eCSSProperty_list_style_type: return ParseListStyleType(aValue); case eCSSProperty_marks: return ParseMarks(aValue); + case eCSSProperty_ruby_position: + return ParseRubyPosition(aValue); case eCSSProperty_text_align: return ParseTextAlign(aValue); case eCSSProperty_text_align_last: return ParseTextAlignLast(aValue); case eCSSProperty_text_decoration_line: return ParseTextDecorationLine(aValue); case eCSSProperty_text_combine_upright: return ParseTextCombineUpright(aValue); @@ -13120,16 +13123,44 @@ CSSParserImpl::ParseQuotes() quotes->mNext = new nsCSSValuePairList; quotes = quotes->mNext; } } AppendValue(eCSSProperty_quotes, value); return true; } +static const int32_t gRubyPositionMask[] = { + // vertical values + NS_STYLE_RUBY_POSITION_OVER | + NS_STYLE_RUBY_POSITION_UNDER | + NS_STYLE_RUBY_POSITION_INTER_CHARACTER, + // horizontal values + NS_STYLE_RUBY_POSITION_RIGHT | + NS_STYLE_RUBY_POSITION_LEFT, + // end + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseRubyPosition(nsCSSValue& aValue) +{ + if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + return true; + } + if (!ParseBitmaskValues(aValue, nsCSSProps::kRubyPositionKTable, + gRubyPositionMask)) { + return false; + } + auto value = aValue.GetIntValue(); + // The specified value must include *both* a vertical keyword *and* + // a horizontal keyword. We reject it here if either is missing. + return (value & gRubyPositionMask[0]) && (value & gRubyPositionMask[1]); +} + bool CSSParserImpl::ParseSize() { nsCSSValue width, height; if (!ParseVariant(width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) { return false; } if (width.IsLengthUnit()) {
--- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -2971,16 +2971,27 @@ CSS_PROP_POSITION( CSS_PROPERTY_STORES_CALC | CSS_PROPERTY_UNITLESS_LENGTH_QUIRK | CSS_PROPERTY_GETCS_NEEDS_LAYOUT_FLUSH, "", VARIANT_AHLP | VARIANT_CALC, nullptr, offsetof(nsStylePosition, mOffset), eStyleAnimType_Sides_Right) +CSS_PROP_TEXT( + ruby-position, + ruby_position, + RubyPosition, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "layout.css.ruby.enabled", + 0, + kRubyPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_DISPLAY( scroll-behavior, scroll_behavior, ScrollBehavior, CSS_PROPERTY_PARSE_VALUE, "layout.css.scroll-behavior.property-enabled", VARIANT_HK, kScrollBehaviorKTable,
--- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1620,16 +1620,26 @@ const KTableValue nsCSSProps::kRadialGra const KTableValue nsCSSProps::kResizeKTable[] = { eCSSKeyword_none, NS_STYLE_RESIZE_NONE, eCSSKeyword_both, NS_STYLE_RESIZE_BOTH, eCSSKeyword_horizontal, NS_STYLE_RESIZE_HORIZONTAL, eCSSKeyword_vertical, NS_STYLE_RESIZE_VERTICAL, eCSSKeyword_UNKNOWN,-1 }; +const KTableValue nsCSSProps::kRubyPositionKTable[] = { + eCSSKeyword_over, NS_STYLE_RUBY_POSITION_OVER, + eCSSKeyword_under, NS_STYLE_RUBY_POSITION_UNDER, + // bug 1055672 for 'inter-character' support + // eCSSKeyword_inter_character, NS_STYLE_RUBY_POSITION_INTER_CHARACTER, + eCSSKeyword_right, NS_STYLE_RUBY_POSITION_RIGHT, + eCSSKeyword_left, NS_STYLE_RUBY_POSITION_LEFT, + eCSSKeyword_UNKNOWN, -1 +}; + const KTableValue nsCSSProps::kScrollBehaviorKTable[] = { eCSSKeyword_auto, NS_STYLE_SCROLL_BEHAVIOR_AUTO, eCSSKeyword_smooth, NS_STYLE_SCROLL_BEHAVIOR_SMOOTH, eCSSKeyword_UNKNOWN,-1 }; const KTableValue nsCSSProps::kStackSizingKTable[] = { eCSSKeyword_ignore, NS_STYLE_STACK_SIZING_IGNORE,
--- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -623,16 +623,17 @@ public: static const KTableValue kPointerEventsKTable[]; // Not const because we modify its entries when the pref // "layout.css.sticky.enabled" changes: static KTableValue kPositionKTable[]; static const KTableValue kRadialGradientShapeKTable[]; static const KTableValue kRadialGradientSizeKTable[]; static const KTableValue kRadialGradientLegacySizeKTable[]; static const KTableValue kResizeKTable[]; + static const KTableValue kRubyPositionKTable[]; static const KTableValue kScrollBehaviorKTable[]; static const KTableValue kSpeakKTable[]; static const KTableValue kSpeakHeaderKTable[]; static const KTableValue kSpeakNumeralKTable[]; static const KTableValue kSpeakPunctuationKTable[]; static const KTableValue kSpeechRateKTable[]; static const KTableValue kStackSizingKTable[]; static const KTableValue kTableLayoutKTable[];
--- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -1285,16 +1285,23 @@ nsCSSValue::AppendToString(nsCSSProperty break; case eCSSProperty_clip_path: AppendASCIItoUTF16(nsCSSProps::ValueToKeyword(intValue, nsCSSProps::kClipShapeSizingKTable), aResult); break; + case eCSSProperty_ruby_position: + nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, + NS_STYLE_RUBY_POSITION_OVER, + NS_STYLE_RUBY_POSITION_LEFT, + aResult); + break; + default: const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue); AppendASCIItoUTF16(name, aResult); break; } } else if (eCSSUnit_EnumColor == unit) { // we can lookup the property in the ColorTable and then
--- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -3072,16 +3072,30 @@ nsComputedDOMStyle::DoGetLineHeight() SetValueToCoord(val, StyleText()->mLineHeight, true, nullptr, nsCSSProps::kLineHeightKTable); } return val; } CSSValue* +nsComputedDOMStyle::DoGetRubyPosition() +{ + nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; + int32_t intValue = StyleText()->mRubyPosition; + nsAutoString valueStr; + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_ruby_position, + intValue, + NS_STYLE_RUBY_POSITION_OVER, + NS_STYLE_RUBY_POSITION_LEFT, valueStr); + val->SetString(valueStr); + return val; +} + +CSSValue* nsComputedDOMStyle::DoGetVerticalAlign() { nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; SetValueToCoord(val, StyleTextReset()->mVerticalAlign, false, &nsComputedDOMStyle::GetLineHeightCoord, nsCSSProps::kVerticalAlignKTable); return val; }
--- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -369,16 +369,17 @@ private: /* List properties */ mozilla::dom::CSSValue* DoGetListStyleImage(); mozilla::dom::CSSValue* DoGetListStylePosition(); mozilla::dom::CSSValue* DoGetListStyleType(); mozilla::dom::CSSValue* DoGetImageRegion(); /* Text Properties */ mozilla::dom::CSSValue* DoGetLineHeight(); + mozilla::dom::CSSValue* DoGetRubyPosition(); mozilla::dom::CSSValue* DoGetTextAlign(); mozilla::dom::CSSValue* DoGetTextAlignLast(); mozilla::dom::CSSValue* DoGetTextCombineUpright(); mozilla::dom::CSSValue* DoGetTextDecoration(); mozilla::dom::CSSValue* DoGetTextDecorationColor(); mozilla::dom::CSSValue* DoGetTextDecorationLine(); mozilla::dom::CSSValue* DoGetTextDecorationStyle(); mozilla::dom::CSSValue* DoGetTextIndent();
--- a/layout/style/nsComputedDOMStylePropertyList.h +++ b/layout/style/nsComputedDOMStylePropertyList.h @@ -192,16 +192,17 @@ COMPUTED_STYLE_PROP(page_break_before, COMPUTED_STYLE_PROP(page_break_inside, PageBreakInside) COMPUTED_STYLE_PROP(perspective, Perspective) COMPUTED_STYLE_PROP(perspective_origin, PerspectiveOrigin) COMPUTED_STYLE_PROP(pointer_events, PointerEvents) COMPUTED_STYLE_PROP(position, Position) COMPUTED_STYLE_PROP(quotes, Quotes) COMPUTED_STYLE_PROP(resize, Resize) COMPUTED_STYLE_PROP(right, Right) +COMPUTED_STYLE_PROP(ruby_position, RubyPosition) COMPUTED_STYLE_PROP(scroll_behavior, ScrollBehavior) //// COMPUTED_STYLE_PROP(size, Size) COMPUTED_STYLE_PROP(table_layout, TableLayout) COMPUTED_STYLE_PROP(text_align, TextAlign) COMPUTED_STYLE_PROP(text_combine_upright, TextCombineUpright) COMPUTED_STYLE_PROP(text_decoration, TextDecoration) COMPUTED_STYLE_PROP(text_decoration_color, TextDecorationColor) COMPUTED_STYLE_PROP(text_decoration_line, TextDecorationLine)
--- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -4363,16 +4363,23 @@ nsRuleNode::ComputeTextData(void* aStart NS_STYLE_WORDWRAP_NORMAL, 0, 0, 0, 0); // hyphens: enum, inherit, initial SetDiscrete(*aRuleData->ValueForHyphens(), text->mHyphens, canStoreInRuleTree, SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, parentText->mHyphens, NS_STYLE_HYPHENS_MANUAL, 0, 0, 0, 0); + // ruby-position: enum, inherit, initial + SetDiscrete(*aRuleData->ValueForRubyPosition(), + text->mRubyPosition, canStoreInRuleTree, + SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT, + parentText->mRubyPosition, + NS_STYLE_RUBY_POSITION_INITIAL, 0, 0, 0, 0); + // text-size-adjust: none, auto, inherit, initial SetDiscrete(*aRuleData->ValueForTextSizeAdjust(), text->mTextSizeAdjust, canStoreInRuleTree, SETDSC_NONE | SETDSC_AUTO | SETDSC_UNSET_INHERIT, parentText->mTextSizeAdjust, NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // initial value NS_STYLE_TEXT_SIZE_ADJUST_AUTO, // auto value NS_STYLE_TEXT_SIZE_ADJUST_NONE, // none value
--- a/layout/style/nsStyleConsts.h +++ b/layout/style/nsStyleConsts.h @@ -838,16 +838,25 @@ static inline mozilla::css::Side operato #define NS_STYLE_WORDWRAP_NORMAL 0 #define NS_STYLE_WORDWRAP_BREAK_WORD 1 // See nsStyleText #define NS_STYLE_HYPHENS_NONE 0 #define NS_STYLE_HYPHENS_MANUAL 1 #define NS_STYLE_HYPHENS_AUTO 2 +// ruby-position, see nsStyleText +#define NS_STYLE_RUBY_POSITION_OVER 0x01 +#define NS_STYLE_RUBY_POSITION_UNDER 0x02 +#define NS_STYLE_RUBY_POSITION_INTER_CHARACTER 0x04 // placeholder, not yet parsed +#define NS_STYLE_RUBY_POSITION_RIGHT 0x08 +#define NS_STYLE_RUBY_POSITION_LEFT 0x10 +#define NS_STYLE_RUBY_POSITION_INITIAL \ + (NS_STYLE_RUBY_POSITION_OVER | NS_STYLE_RUBY_POSITION_RIGHT) + // See nsStyleText #define NS_STYLE_TEXT_SIZE_ADJUST_NONE 0 #define NS_STYLE_TEXT_SIZE_ADJUST_AUTO 1 // See nsStyleText #define NS_STYLE_TEXT_ORIENTATION_MIXED 0 #define NS_STYLE_TEXT_ORIENTATION_UPRIGHT 1 #define NS_STYLE_TEXT_ORIENTATION_SIDEWAYS_RIGHT 2
--- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -3339,16 +3339,17 @@ nsStyleText::nsStyleText(void) mTextAlignLast = NS_STYLE_TEXT_ALIGN_AUTO; mTextAlignTrue = false; mTextAlignLastTrue = false; mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE; mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL; mWordBreak = NS_STYLE_WORDBREAK_NORMAL; mWordWrap = NS_STYLE_WORDWRAP_NORMAL; mHyphens = NS_STYLE_HYPHENS_MANUAL; + mRubyPosition = NS_STYLE_RUBY_POSITION_INITIAL; mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO; mTextCombineUpright = NS_STYLE_TEXT_COMBINE_UPRIGHT_NONE; mControlCharacterVisibility = NS_STYLE_CONTROL_CHARACTER_VISIBILITY_HIDDEN; mLetterSpacing.SetNormalValue(); mLineHeight.SetNormalValue(); mTextIndent.SetCoordValue(0); mWordSpacing = 0; @@ -3362,16 +3363,17 @@ nsStyleText::nsStyleText(const nsStyleTe mTextAlignLast(aSource.mTextAlignLast), mTextAlignTrue(false), mTextAlignLastTrue(false), mTextTransform(aSource.mTextTransform), mWhiteSpace(aSource.mWhiteSpace), mWordBreak(aSource.mWordBreak), mWordWrap(aSource.mWordWrap), mHyphens(aSource.mHyphens), + mRubyPosition(aSource.mRubyPosition), mTextSizeAdjust(aSource.mTextSizeAdjust), mTextCombineUpright(aSource.mTextCombineUpright), mControlCharacterVisibility(aSource.mControlCharacterVisibility), mTabSize(aSource.mTabSize), mWordSpacing(aSource.mWordSpacing), mLetterSpacing(aSource.mLetterSpacing), mLineHeight(aSource.mLineHeight), mTextIndent(aSource.mTextIndent), @@ -3402,16 +3404,17 @@ nsChangeHint nsStyleText::CalcDifference (mTextAlignLast != aOther.mTextAlignLast) || (mTextAlignTrue != aOther.mTextAlignTrue) || (mTextAlignLastTrue != aOther.mTextAlignLastTrue) || (mTextTransform != aOther.mTextTransform) || (mWhiteSpace != aOther.mWhiteSpace) || (mWordBreak != aOther.mWordBreak) || (mWordWrap != aOther.mWordWrap) || (mHyphens != aOther.mHyphens) || + (mRubyPosition != aOther.mRubyPosition) || (mTextSizeAdjust != aOther.mTextSizeAdjust) || (mLetterSpacing != aOther.mLetterSpacing) || (mLineHeight != aOther.mLineHeight) || (mTextIndent != aOther.mTextIndent) || (mWordSpacing != aOther.mWordSpacing) || (mTabSize != aOther.mTabSize)) return NS_STYLE_HINT_REFLOW;
--- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1591,16 +1591,17 @@ struct nsStyleText { uint8_t mTextAlignLast; // [inherited] see nsStyleConsts.h bool mTextAlignTrue : 1; // [inherited] see nsStyleConsts.h bool mTextAlignLastTrue : 1; // [inherited] see nsStyleConsts.h uint8_t mTextTransform; // [inherited] see nsStyleConsts.h uint8_t mWhiteSpace; // [inherited] see nsStyleConsts.h uint8_t mWordBreak; // [inherited] see nsStyleConsts.h uint8_t mWordWrap; // [inherited] see nsStyleConsts.h uint8_t mHyphens; // [inherited] see nsStyleConsts.h + uint8_t mRubyPosition; // [inherited] see nsStyleConsts.h uint8_t mTextSizeAdjust; // [inherited] see nsStyleConsts.h uint8_t mTextCombineUpright; // [inherited] see nsStyleConsts.h uint8_t mControlCharacterVisibility; // [inherited] see nsStyleConsts.h int32_t mTabSize; // [inherited] see nsStyleConsts.h nscoord mWordSpacing; // [inherited] nsStyleCoord mLetterSpacing; // [inherited] coord, normal nsStyleCoord mLineHeight; // [inherited] coord, factor, normal
--- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -5042,16 +5042,31 @@ if (SpecialPowers.getBoolPref("layout.cs // Using unshift to add these values at the beginning. // Adding them to the end would trigger bug 1038905. The "unshift" should be // changed to a "push" when this bug is resolved. gCSSProperties["display"].other_values.unshift("ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container"); + gCSSProperties["ruby-position"] = { + domProp: "rubyPosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "over right", "right over" ], + other_values: [ + "over left", "under right", "under left", + "left over", "right under", "left under" + ], + invalid_values: [ + "over", "under", "left", "right", "auto", "none", "not_a_position", + "over over", "over under", "left left", "left right", + "over left over", "right over left", "0", "100px", "50%" + ] + }; } if (SpecialPowers.getBoolPref("layout.css.grid.enabled")) { gCSSProperties["display"].other_values.push("grid", "inline-grid"); gCSSProperties["grid-auto-flow"] = { domProp: "gridAutoFlow", inherited: false, type: CSS_TYPE_LONGHAND,
new file mode 100644 --- /dev/null +++ b/media/libstagefright/binding/H264.cpp @@ -0,0 +1,356 @@ +/* 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 "mozilla/ArrayUtils.h" +#include "mp4_demuxer/AnnexB.h" +#include "mp4_demuxer/ByteReader.h" +#include "mp4_demuxer/ByteWriter.h" +#include "mp4_demuxer/H264.h" +#include <media/stagefright/foundation/ABitReader.h> + +using namespace mozilla; + +namespace mp4_demuxer +{ + +class BitReader +{ +public: + explicit BitReader(const ByteBuffer& aBuffer) + : mBitReader(aBuffer.Elements(), aBuffer.Length()) + { + } + + uint32_t ReadBits(size_t aNum) + { + MOZ_ASSERT(mBitReader.numBitsLeft()); + MOZ_ASSERT(aNum <= 32); + if (mBitReader.numBitsLeft() < aNum) { + return 0; + } + return mBitReader.getBits(aNum); + } + + uint32_t ReadBit() + { + return ReadBits(1); + } + + // Read unsigned integer Exp-Golomb-coded. + uint32_t ReadUE() + { + uint32_t i = 0; + + while (ReadBit() == 0 && i < 32) { + i++; + } + if (i == 32) { + MOZ_ASSERT(false); + return 0; + } + uint32_t r = ReadBits(i); + r += (1 << i) - 1; + return r; + } + + // Read signed integer Exp-Golomb-coded. + int32_t ReadSE() + { + int32_t r = ReadUE(); + if (r & 1) { + return (r+1) / 2; + } else { + return -r / 2; + } + } + +private: + stagefright::ABitReader mBitReader; +}; + +SPSData::SPSData() +{ + PodZero(this); + chroma_format_idc = 1; +} + +/* static */ already_AddRefed<ByteBuffer> +H264::DecodeNALUnit(const ByteBuffer* aNAL) +{ + MOZ_ASSERT(aNAL); + + if (aNAL->Length() < 4) { + return nullptr; + } + + nsRefPtr<ByteBuffer> rbsp = new ByteBuffer; + ByteReader reader(*aNAL); + uint8_t nal_unit_type = reader.ReadU8() & 0x1f; + uint32_t nalUnitHeaderBytes = 1; + if (nal_unit_type == 14 || nal_unit_type == 20 || nal_unit_type == 21) { + bool svc_extension_flag = false; + bool avc_3d_extension_flag = false; + if (nal_unit_type != 21) { + svc_extension_flag = reader.PeekU8() & 0x80; + } else { + avc_3d_extension_flag = reader.PeekU8() & 0x80; + } + if (svc_extension_flag) { + nalUnitHeaderBytes += 3; + } else if (avc_3d_extension_flag) { + nalUnitHeaderBytes += 2; + } else { + nalUnitHeaderBytes += 3; + } + } + if (!reader.Read(nalUnitHeaderBytes - 1)) { + return nullptr; + } + uint32_t zeros = 0; + while (reader.Remaining()) { + uint8_t byte = reader.ReadU8(); + if (zeros < 2 || byte == 0x03) { + rbsp->AppendElement(byte); + } + if (byte == 0) { + zeros++; + } else { + zeros = 0; + } + } + return rbsp.forget(); +} + +/* static */ bool +H264::DecodeSPS(const ByteBuffer* aSPS, SPSData& aDest) +{ + MOZ_ASSERT(aSPS); + BitReader br(*aSPS); + + int32_t lastScale; + int32_t nextScale; + int32_t deltaScale; + + aDest.profile_idc = br.ReadBits(8); + aDest.constraint_set0_flag = br.ReadBit(); + aDest.constraint_set1_flag = br.ReadBit(); + aDest.constraint_set2_flag = br.ReadBit(); + aDest.constraint_set3_flag = br.ReadBit(); + aDest.constraint_set4_flag = br.ReadBit(); + aDest.constraint_set5_flag = br.ReadBit(); + br.ReadBits(2); // reserved_zero_2bits + aDest.level_idc = br.ReadBits(8); + aDest.seq_parameter_set_id = br.ReadUE(); + if (aDest.profile_idc == 100 || aDest.profile_idc == 110 || + aDest.profile_idc == 122 || aDest.profile_idc == 244 || + aDest.profile_idc == 44 || aDest.profile_idc == 83 || + aDest.profile_idc == 86 || aDest.profile_idc == 118 || + aDest.profile_idc == 128 || aDest.profile_idc == 138 || + aDest.profile_idc == 139 || aDest.profile_idc == 134) { + if ((aDest.chroma_format_idc = br.ReadUE()) == 3) { + aDest.separate_colour_plane_flag = br.ReadBit(); + } + br.ReadUE(); // bit_depth_luma_minus8 + br.ReadUE(); // bit_depth_chroma_minus8 + br.ReadBit(); // qpprime_y_zero_transform_bypass_flag + if (br.ReadBit()) { // seq_scaling_matrix_present_flag + for (int idx = 0; idx < ((aDest.chroma_format_idc != 3) ? 8 : 12); ++idx) { + if (br.ReadBit()) { // Scaling list present + lastScale = nextScale = 8; + int sl_n = (idx < 6) ? 16 : 64; + for (int sl_i = 0; sl_i < sl_n; sl_i++) { + if (nextScale) { + deltaScale = br.ReadSE(); + nextScale = (lastScale + deltaScale + 256) % 256; + } + lastScale = (nextScale == 0) ? lastScale : nextScale; + } + } + } + } + } + aDest.log2_max_frame_num = br.ReadUE() + 4; + aDest.pic_order_cnt_type = br.ReadUE(); + if (aDest.pic_order_cnt_type == 0) { + aDest.log2_max_pic_order_cnt_lsb = br.ReadUE() + 4; + } else if (aDest.pic_order_cnt_type == 1) { + aDest.delta_pic_order_always_zero_flag = br.ReadBit(); + aDest.offset_for_non_ref_pic = br.ReadSE(); + aDest.offset_for_top_to_bottom_field = br.ReadSE(); + uint32_t num_ref_frames_in_pic_order_cnt_cycle = br.ReadUE(); + for (uint32_t i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; i++) { + br.ReadSE(); // offset_for_ref_frame[i] + } + } + aDest.max_num_ref_frames = br.ReadUE(); + aDest.gaps_in_frame_num_allowed_flag = br.ReadBit(); + aDest.pic_width_in_mbs = br.ReadUE() + 1; + aDest.pic_height_in_map_units = br.ReadUE() + 1; + aDest.frame_mbs_only_flag = br.ReadBit(); + if (!aDest.frame_mbs_only_flag) { + aDest.pic_height_in_map_units *= 2; + aDest.mb_adaptive_frame_field_flag = br.ReadBit(); + } + br.ReadBit(); // direct_8x8_inference_flag + aDest.frame_cropping_flag = br.ReadBit(); + if (aDest.frame_cropping_flag) { + aDest.frame_crop_left_offset = br.ReadUE(); + aDest.frame_crop_right_offset = br.ReadUE(); + aDest.frame_crop_top_offset = br.ReadUE(); + aDest.frame_crop_bottom_offset = br.ReadUE(); + } + + // Calculate common values. + + // FFmpeg and VLC ignore the left and top cropping. Do the same here. + + uint8_t ChromaArrayType = + aDest.separate_colour_plane_flag ? 0 : aDest.chroma_format_idc; + // Calculate width. + uint32_t CropUnitX = 1; + uint32_t SubWidthC = aDest.chroma_format_idc == 3 ? 1 : 2; + if (ChromaArrayType != 0) { + CropUnitX = SubWidthC; + } + uint32_t cropX = CropUnitX * aDest.frame_crop_right_offset; + aDest.pic_width = aDest.pic_width_in_mbs * 16 - cropX; + + // Calculate Height + uint32_t CropUnitY = 2 - aDest.frame_mbs_only_flag; + uint32_t SubHeightC = aDest.chroma_format_idc <= 1 ? 2 : 1; + if (ChromaArrayType != 0) + CropUnitY *= SubHeightC; + uint32_t cropY = CropUnitY * aDest.frame_crop_bottom_offset; + aDest.pic_height = aDest.pic_height_in_map_units * 16 - cropY; + + aDest.interlaced = !aDest.frame_mbs_only_flag; + return true; +} + +/* static */ void +H264::vui_parameters(BitReader& aBr, SPSData& aDest) +{ + aDest.aspect_ratio_info_present_flag = aBr.ReadBit(); + if (aDest.aspect_ratio_info_present_flag) + { + aDest.aspect_ratio_idc = aBr.ReadBits(8); + + if (aDest.aspect_ratio_idc == 255 /* EXTENDED_SAR */) { + aDest.sar_width = aBr.ReadBits(16); + aDest.sar_height = aBr.ReadBits(16); + } + } + else { + aDest.sar_width = aDest.sar_height = 0; + } + + if (aBr.ReadBit()) { //overscan_info_present_flag + aDest.overscan_appropriate_flag = aBr.ReadBit(); + } + if (aBr.ReadBit()) { //video_signal_type_present_flag + aDest.video_format = aBr.ReadBits(3); + aDest.video_full_range_flag = aBr.ReadBit(); + aDest.colour_description_present_flag = aBr.ReadBit(); + if (aDest.colour_description_present_flag) { + aDest.colour_primaries = aBr.ReadBits(8); + aDest.transfer_characteristics = aBr.ReadBits(8); + aDest.matrix_coefficients = aBr.ReadBits(8); + } + } + aDest.chroma_loc_info_present_flag = aBr.ReadBit(); + + if (aDest.chroma_loc_info_present_flag) { + aDest.chroma_sample_loc_type_top_field = aBr.ReadUE(); + aDest.chroma_sample_loc_type_bottom_field = aBr.ReadUE(); + } + + if (aBr.ReadBit()) { //timing_info_present_flag + aDest.num_units_in_tick = aBr.ReadBits(32); + aDest.time_scale = aBr.ReadBits(32); + aDest.fixed_frame_rate_flag = aBr.ReadBit(); + } + + bool hrd_present = false; + if (aBr.ReadBit()) { // nal_hrd_parameters_present_flag + hrd_parameters(aBr); + hrd_present = true; + } + if (aBr.ReadBit()) { // vcl_hrd_parameters_present_flag + hrd_parameters(aBr); + hrd_present = true; + } + if (hrd_present) { + aBr.ReadBit(); // low_delay_hrd_flag + } + aDest.pic_struct_present_flag = aBr.ReadBit(); + aDest.bitstream_restriction_flag = aBr.ReadBit(); + if (aDest.bitstream_restriction_flag) { + aDest.motion_vectors_over_pic_boundaries_flag = aBr.ReadBit(); + aDest.max_bytes_per_pic_denom = aBr.ReadUE(); + aDest.max_bits_per_mb_denom = aBr.ReadUE(); + aDest.log2_max_mv_length_horizontal = aBr.ReadUE(); + aDest.log2_max_mv_length_vertical = aBr.ReadUE(); + aDest.max_num_reorder_frames = aBr.ReadUE(); + aDest.max_dec_frame_buffering = aBr.ReadUE(); + } +} + +/* static */ void +H264::hrd_parameters(BitReader& aBr) +{ + uint32_t cpb_cnt_minus1 = aBr.ReadUE(); + aBr.ReadBits(4); // bit_rate_scale + aBr.ReadBits(4); // cpb_size_scale + for (uint32_t SchedSelIdx = 0; SchedSelIdx <= cpb_cnt_minus1; SchedSelIdx++) { + aBr.ReadUE(); // bit_rate_value_minus1[ SchedSelIdx ] + aBr.ReadUE(); // cpb_size_value_minus1[ SchedSelIdx ] + aBr.ReadBit(); // cbr_flag[ SchedSelIdx ] + } + aBr.ReadBits(5); // initial_cpb_removal_delay_length_minus1 + aBr.ReadBits(5); // cpb_removal_delay_length_minus1 + aBr.ReadBits(5); // dpb_output_delay_length_minus1 + aBr.ReadBits(5); // time_offset_length +} + +/* static */ bool +H264::DecodeSPSFromExtraData(const ByteBuffer* aExtraData, SPSData& aDest) +{ + if (!AnnexB::HasSPS(aExtraData)) { + return false; + } + ByteReader reader(*aExtraData); + + if (!reader.Read(5)) { + return false; + } + + if (!(reader.ReadU8() & 0x1f)) { + // No SPS. + reader.DiscardRemaining(); + return false; + } + uint16_t length = reader.ReadU16(); + + if ((reader.PeekU8() & 0x1f) != 7) { + // Not a SPS NAL type. + reader.DiscardRemaining(); + return false; + } + + const uint8_t* ptr = reader.Read(length); + if (!ptr) { + return false; + } + + nsRefPtr<ByteBuffer> rawNAL = new ByteBuffer; + rawNAL->AppendElements(ptr, length); + + nsRefPtr<ByteBuffer> sps = DecodeNALUnit(rawNAL); + + reader.DiscardRemaining(); + + return DecodeSPS(sps, aDest); +} + +} // namespace mp4_demuxer
new file mode 100644 --- /dev/null +++ b/media/libstagefright/binding/include/mp4_demuxer/H264.h @@ -0,0 +1,361 @@ +/* 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 MP4_DEMUXER_H264_H_ +#define MP4_DEMUXER_H264_H_ + +#include "mp4_demuxer/DecoderData.h" + +namespace mp4_demuxer +{ + +class BitReader; + +struct SPSData +{ + /* Decoded Members */ + /* + pic_width is the decoded width according to: + pic_width = ((pic_width_in_mbs_minus1 + 1) * 16) + - (frame_crop_left_offset + frame_crop_right_offset) * 2 + */ + uint32_t pic_width; + /* + pic_height is the decoded height according to: + pic_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16) + - (frame_crop_top_offset + frame_crop_bottom_offset) * 2 + */ + uint32_t pic_height; + + bool interlaced; + + /* + H264 decoding parameters according to ITU-T H.264 (T-REC-H.264-201402-I/en) + http://www.itu.int/rec/T-REC-H.264-201402-I/en + */ + + bool constraint_set0_flag; + bool constraint_set1_flag; + bool constraint_set2_flag; + bool constraint_set3_flag; + bool constraint_set4_flag; + bool constraint_set5_flag; + + /* + profile_idc and level_idc indicate the profile and level to which the coded + video sequence conforms when the SVC sequence parameter set is the active + SVC sequence parameter set. + */ + uint8_t profile_idc; + uint8_t level_idc; + + /* + seq_parameter_set_id identifies the sequence parameter set that is referred + to by the picture parameter set. The value of seq_parameter_set_id shall be + in the range of 0 to 31, inclusive. + */ + uint8_t seq_parameter_set_id; + + /* + When the value of chroma_format_idc is equal to 1, the nominal vertical + and horizontal relative locations of luma and chroma samples in frames are + shown in Figure 6-1. Alternative chroma sample relative locations may be + indicated in video usability information (see Annex E). + */ + uint8_t chroma_format_idc; + + /* + If separate_colour_plane_flag is equal to 0, each of the two chroma arrays + has the same height and width as the luma array. Otherwise + (separate_colour_plane_flag is equal to 1), the three colour planes are + separately processed as monochrome sampled pictures. + */ + bool separate_colour_plane_flag; + + /* + log2_max_frame_num_minus4 specifies the value of the variable + MaxFrameNum that is used in frame_num related derivations as + follows: + + MaxFrameNum = 2( log2_max_frame_num_minus4 + 4 ). The value of + log2_max_frame_num_minus4 shall be in the range of 0 to 12, inclusive. + */ + uint8_t log2_max_frame_num; + + /* + pic_order_cnt_type specifies the method to decode picture order + count (as specified in subclause 8.2.1). The value of + pic_order_cnt_type shall be in the range of 0 to 2, inclusive. + */ + uint8_t pic_order_cnt_type; + + /* + log2_max_pic_order_cnt_lsb_minus4 specifies the value of the + variable MaxPicOrderCntLsb that is used in the decoding + process for picture order count as specified in subclause + 8.2.1 as follows: + + MaxPicOrderCntLsb = 2( log2_max_pic_order_cnt_lsb_minus4 + 4 ) + + The value of log2_max_pic_order_cnt_lsb_minus4 shall be in + the range of 0 to 12, inclusive. + */ + uint8_t log2_max_pic_order_cnt_lsb; + + /* + delta_pic_order_always_zero_flag equal to 1 specifies that + delta_pic_order_cnt[ 0 ] and delta_pic_order_cnt[ 1 ] are + not present in the slice headers of the sequence and shall + be inferred to be equal to 0. + */ + bool delta_pic_order_always_zero_flag; + + /* + offset_for_non_ref_pic is used to calculate the picture + order count of a non-reference picture as specified in + 8.2.1. The value of offset_for_non_ref_pic shall be in the + range of -231 to 231 - 1, inclusive. + */ + int8_t offset_for_non_ref_pic; + + /* + offset_for_top_to_bottom_field is used to calculate the + picture order count of a bottom field as specified in + subclause 8.2.1. The value of offset_for_top_to_bottom_field + shall be in the range of -231 to 231 - 1, inclusive. + */ + int8_t offset_for_top_to_bottom_field; + + /* + max_num_ref_frames specifies the maximum number of short-term and + long-term reference frames, complementary reference field pairs, + and non-paired reference fields that may be used by the decoding + process for inter prediction of any picture in the + sequence. max_num_ref_frames also determines the size of the sliding + window operation as specified in subclause 8.2.5.3. The value of + max_num_ref_frames shall be in the range of 0 to MaxDpbSize (as + specified in subclause A.3.1 or A.3.2), inclusive. + */ + uint32_t max_num_ref_frames; + + /* + gaps_in_frame_num_value_allowed_flag specifies the allowed + values of frame_num as specified in subclause 7.4.3 and the + decoding process in case of an inferred gap between values of + frame_num as specified in subclause 8.2.5.2. + */ + bool gaps_in_frame_num_allowed_flag; + + /* + pic_width_in_mbs_minus1 plus 1 specifies the width of each + decoded picture in units of macroblocks. 16 macroblocks in a row + */ + uint32_t pic_width_in_mbs; + + /* + pic_height_in_map_units_minus1 plus 1 specifies the height in + slice group map units of a decoded frame or field. 16 + macroblocks in each column. + */ + uint32_t pic_height_in_map_units; + + /* + frame_mbs_only_flag equal to 0 specifies that coded pictures of + the coded video sequence may either be coded fields or coded + frames. frame_mbs_only_flag equal to 1 specifies that every + coded picture of the coded video sequence is a coded frame + containing only frame macroblocks. + */ + bool frame_mbs_only_flag; + + /* + mb_adaptive_frame_field_flag equal to 0 specifies no + switching between frame and field macroblocks within a + picture. mb_adaptive_frame_field_flag equal to 1 specifies + the possible use of switching between frame and field + macroblocks within frames. When mb_adaptive_frame_field_flag + is not present, it shall be inferred to be equal to 0. + */ + bool mb_adaptive_frame_field_flag; + + /* + frame_cropping_flag equal to 1 specifies that the frame cropping + offset parameters follow next in the sequence parameter + set. frame_cropping_flag equal to 0 specifies that the frame + cropping offset parameters are not present. + */ + bool frame_cropping_flag; + uint32_t frame_crop_left_offset;; + uint32_t frame_crop_right_offset; + uint32_t frame_crop_top_offset; + uint32_t frame_crop_bottom_offset; + + // VUI Parameters + + /* + vui_parameters_present_flag equal to 1 specifies that the + vui_parameters( ) syntax structure as specified in Annex E is + present. vui_parameters_present_flag equal to 0 specifies that + the vui_parameters( ) syntax structure as specified in Annex E + is not present. + */ + bool vui_parameters_present_flag; + + /* + aspect_ratio_info_present_flag equal to 1 specifies that + aspect_ratio_idc is present. aspect_ratio_info_present_flag + equal to 0 specifies that aspect_ratio_idc is not present. + */ + bool aspect_ratio_info_present_flag; + + /* + aspect_ratio_idc specifies the value of the sample aspect + ratio of the luma samples. Table E-1 shows the meaning of + the code. When aspect_ratio_idc indicates Extended_SAR, the + sample aspect ratio is represented by sar_width and + sar_height. When the aspect_ratio_idc syntax element is not + present, aspect_ratio_idc value shall be inferred to be + equal to 0. + */ + uint8_t aspect_ratio_idc; + uint32_t sar_width; + uint32_t sar_height; + + bool overscan_info_present_flag; + bool overscan_appropriate_flag; + + uint8_t video_format; + bool video_full_range_flag; + bool colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + bool chroma_loc_info_present_flag; + uint32_t chroma_sample_loc_type_top_field; + uint32_t chroma_sample_loc_type_bottom_field; + uint32_t num_units_in_tick; + uint32_t time_scale; + bool fixed_frame_rate_flag; + + // Bitstream restriction parameters + + /* + pic_struct_present_flag equal to 1 specifies that picture timing SEI + messages (clause D.2.2) are present that include the pic_struct syntax + element. pic_struct_present_flag equal to 0 specifies that the pic_struct + syntax element is not present in picture timing SEI messages. + When pic_struct_present_flag is not present, its value shall be inferred to + be equal to 0. + */ + bool pic_struct_present_flag; + + /* + bitstream_restriction_flag equal to 1, specifies that the following coded + video sequence bitstream restriction parameters are present. + bitstream_restriction_flag equal to 0, specifies that the following coded + video sequence bitstream restriction parameters are not present. + */ + bool bitstream_restriction_flag; + + /* + motion_vectors_over_pic_boundaries_flag equal to 0 indicates that no + sample outside the picture boundaries and no sample at a fractional + sample position for which the sample value is derived using one or more + samples outside the picture boundaries is used for inter prediction of any + sample. motion_vectors_over_pic_boundaries_flag equal to 1 indicates that + one or more samples outside picture boundaries may be used in inter + prediction. When the motion_vectors_over_pic_boundaries_flag syntax element + is not present, motion_vectors_over_pic_boundaries_flag value shall be + inferred to be equal to 1. + */ + bool motion_vectors_over_pic_boundaries_flag; + + /* + max_bytes_per_pic_denom indicates a number of bytes not exceeded by the + sum of the sizes of the VCL NAL units associated with any coded picture in + the coded video sequence. + */ + uint32_t max_bytes_per_pic_denom; + + /* + max_bits_per_mb_denom indicates an upper bound for the number of coded bits + of macroblock_layer( ) data for any macroblock in any picture of the coded + video sequence. The value of max_bits_per_mb_denom shall be in the range + of 0 to 16, inclusive. + */ + uint32_t max_bits_per_mb_denom; + + /* + log2_max_mv_length_horizontal and log2_max_mv_length_vertical indicate the + maximum absolute value of a decoded horizontal and vertical motion vector + component, respectively, in 1⁄4 luma sample units, for all pictures in the + coded video sequence. A value of n asserts that no value of a motion vector + component shall exceed the range from −2n to 2n − 1, inclusive, in units + of 1⁄4 luma sample displacement. The value of log2_max_mv_length_horizontal + shall be in the range of 0 to 16, inclusive. The value of + log2_max_mv_length_vertical shall be in the range of 0 to 16, inclusive. + When log2_max_mv_length_horizontal is not present, the values of + log2_max_mv_length_horizontal and log2_max_mv_length_vertical shall be + inferred to be equal to 16. + */ + uint32_t log2_max_mv_length_horizontal; + uint32_t log2_max_mv_length_vertical; + + /* + max_num_reorder_frames indicates an upper bound for the number of frames + buffers, in the decoded picture buffer (DPB), that are required for storing + frames, complementary field pairs, and non-paired fields before output. + It is a requirement of bitstream conformance that the maximum number of + frames, complementary field pairs, or non-paired fields that precede any + frame, complementary field pair, or non-paired field in the coded video + sequence in decoding order and follow it in output order shall be less than + or equal to max_num_reorder_frames. The value of max_num_reorder_frames + shall be in the range of 0 to max_dec_frame_buffering, inclusive. + When the max_num_reorder_frames syntax element is not present, the value + of max_num_reorder_frames value shall be inferred as follows: + – If profile_idc is equal to 44, 86, 100, 110, 122, or 244 and + constraint_set3_flag is equal to 1, the value of max_num_reorder_frames + shall be inferred to be equal to 0. + – Otherwise (profile_idc is not equal to 44, 86, 100, 110, 122, or 244 or + constraint_set3_flag is equal to 0), the value of max_num_reorder_frames + shall be inferred to be equal to MaxDpbFrames. + */ + uint32_t max_num_reorder_frames; + + /* + max_dec_frame_buffering specifies the required size of the HRD decoded + picture buffer (DPB) in units of frame buffers. It is a requirement of + bitstream conformance that the coded video sequence shall not require a + decoded picture buffer with size of more than + Max( 1, max_dec_frame_buffering ) frame buffers to enable the output of + decoded pictures at the output times specified by dpb_output_delay of the + picture timing SEI messages. The value of max_dec_frame_buffering shall be + greater than or equal to max_num_ref_frames. An upper bound for the value + of max_dec_frame_buffering is specified by the level limits in + clauses A.3.1, A.3.2, G.10.2.1, and H.10.2. + */ + uint32_t max_dec_frame_buffering; + + SPSData(); +}; + +class H264 +{ +public: + static bool DecodeSPSFromExtraData(const ByteBuffer* aExtraData, SPSData& aDest); + /* Extract RAW BYTE SEQUENCE PAYLOAD from NAL content. + Returns nullptr if invalid content. */ + static already_AddRefed<ByteBuffer> DecodeNALUnit(const ByteBuffer* aNAL); + /* Decode SPS NAL RBSP and fill SPSData structure */ + static bool DecodeSPS(const ByteBuffer* aSPS, SPSData& aDest); + +private: + static void vui_parameters(BitReader& aBr, SPSData& aDest); + // Read HRD parameters, all data is ignored. + static void hrd_parameters(BitReader& aBr); +}; + +} // namespace mp4_demuxer + +#endif // MP4_DEMUXER_H264_H_
--- a/media/libstagefright/moz.build +++ b/media/libstagefright/moz.build @@ -50,16 +50,17 @@ if CONFIG['OS_TARGET'] != 'Android': EXPORTS.mp4_demuxer += [ 'binding/include/mp4_demuxer/Adts.h', 'binding/include/mp4_demuxer/AnnexB.h', 'binding/include/mp4_demuxer/AtomType.h', 'binding/include/mp4_demuxer/BufferStream.h', 'binding/include/mp4_demuxer/ByteReader.h', 'binding/include/mp4_demuxer/ByteWriter.h', 'binding/include/mp4_demuxer/DecoderData.h', + 'binding/include/mp4_demuxer/H264.h', 'binding/include/mp4_demuxer/Interval.h', 'binding/include/mp4_demuxer/MoofParser.h', 'binding/include/mp4_demuxer/mp4_demuxer.h', ] SOURCES += [ 'frameworks/av/media/libstagefright/foundation/hexdump.cpp', 'frameworks/av/media/libstagefright/MetaData.cpp', @@ -70,16 +71,17 @@ SOURCES += [ ] UNIFIED_SOURCES += [ 'binding/Adts.cpp', 'binding/AnnexB.cpp', 'binding/Box.cpp', 'binding/BufferStream.cpp', 'binding/DecoderData.cpp', + 'binding/H264.cpp', 'binding/Index.cpp', 'binding/MoofParser.cpp', 'binding/mp4_demuxer.cpp', 'frameworks/av/media/libstagefright/DataSource.cpp', 'frameworks/av/media/libstagefright/ESDS.cpp', 'frameworks/av/media/libstagefright/foundation/AAtomizer.cpp', 'frameworks/av/media/libstagefright/foundation/ABitReader.cpp', 'frameworks/av/media/libstagefright/foundation/ABuffer.cpp',
--- a/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.cc +++ b/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.cc @@ -78,17 +78,17 @@ VideoContentMetrics* VPMContentAnalysis: } #endif return ContentMetrics(); } int32_t VPMContentAnalysis::Release() { content_metrics_.reset(NULL); - prev_frame_.reset(NULL); + prev_frame_.reset(); width_ = 0; height_ = 0; first_frame_ = true; return VPM_OK; } @@ -107,17 +107,17 @@ int32_t VPMContentAnalysis::Initialize(i skip_num_ = 2; } // use skipNum = 4 for FULLL_HD images if ( (height_ >= 1080) && (width_ >= 1920) ) { skip_num_ = 4; } content_metrics_.reset(NULL); - prev_frame_.reset(NULL); + prev_frame_.reset(); // Spatial Metrics don't work on a border of 8. Minimum processing // block size is 16 pixels. So make sure the width and height support this. if (width_ <= 32 || height_ <= 32) { return VPM_PARAMETER_ERROR; } content_metrics_.reset(new VideoContentMetrics());
--- a/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.h +++ b/media/webrtc/trunk/webrtc/modules/video_processing/main/source/content_analysis.h @@ -61,17 +61,17 @@ class VPMContentAnalysis { int32_t ComputeSpatialMetrics_C(); #if defined(WEBRTC_ARCH_X86_FAMILY) int32_t ComputeSpatialMetrics_SSE2(); int32_t TemporalDiffMetric_SSE2(); #endif const uint8_t* orig_frame_; - scoped_ptr<uint8_t> prev_frame_; + scoped_ptr<uint8_t[]> prev_frame_; int width_; int height_; int skip_num_; int border_; // Content Metrics: Stores the local average of the metrics. float motion_magnitude_; // motion class float spatial_pred_err_; // spatial class
deleted file mode 120000 --- a/media/webrtc/trunk/webrtc/tools/e2e_quality/audio/perf +++ /dev/null @@ -1,1 +0,0 @@ -../../../../tools/perf \ No newline at end of file
--- a/mfbt/Attributes.h +++ b/mfbt/Attributes.h @@ -507,21 +507,21 @@ * value is allocated on the heap, and will as a result check such allocations * during MOZ_STACK_CLASS and MOZ_NONHEAP_CLASS annotation checking. * MOZ_IMPLICIT: Applies to constructors. Implicit conversion constructors * are disallowed by default unless they are marked as MOZ_IMPLICIT. This * attribute must be used for constructors which intend to provide implicit * conversions. * MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT: Applies to functions. Makes it a compile * time error to pass arithmetic expressions on variables to the function. - * MOZ_STRONG_REF: Applies to declarations of pointer types. This attribute + * MOZ_OWNING_REF: Applies to declarations of pointer types. This attribute * tells the compiler that the raw pointer is a strong reference, and that * property is somehow enforced by the code. This can make the compiler * ignore these pointers when validating the usage of pointers otherwise. - * MOZ_WEAK_REF: Applies to declarations of pointer types. This attribute + * MOZ_NON_OWNING_REF: Applies to declarations of pointer types. This attribute * tells the compiler that the raw pointer is a weak reference, and that * property is somehow enforced by the code. This can make the compiler * ignore these pointers when validating the usage of pointers otherwise. */ #ifdef MOZ_CLANG_PLUGIN # define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override"))) # define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) # define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) @@ -530,18 +530,18 @@ /* in debug builds, these classes do have non-trivial constructors. */ # define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) # else # define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \ MOZ_TRIVIAL_CTOR_DTOR # endif # define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) # define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg"))) -# define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref"))) -# define MOZ_WEAK_REF __attribute__((annotate("moz_weak_ref"))) +# define MOZ_OWNING_REF __attribute__((annotate("moz_strong_ref"))) +# define MOZ_NON_OWNING_REF __attribute__((annotate("moz_weak_ref"))) /* * It turns out that clang doesn't like void func() __attribute__ {} without a * warning, so use pragmas to disable the warning. This code won't work on GCC * anyways, so the warning is safe to ignore. */ # define MOZ_HEAP_ALLOCATOR \ _Pragma("clang diagnostic push") \ _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ @@ -551,18 +551,18 @@ # define MOZ_MUST_OVERRIDE /* nothing */ # define MOZ_STACK_CLASS /* nothing */ # define MOZ_NONHEAP_CLASS /* nothing */ # define MOZ_TRIVIAL_CTOR_DTOR /* nothing */ # define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */ # define MOZ_IMPLICIT /* nothing */ # define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */ # define MOZ_HEAP_ALLOCATOR /* nothing */ -# define MOZ_STRONG_REF /* nothing */ -# define MOZ_WEAK_REF /* nothing */ +# define MOZ_OWNING_REF /* nothing */ +# define MOZ_NON_OWNING_REF /* nothing */ #endif /* MOZ_CLANG_PLUGIN */ /* * MOZ_THIS_IN_INITIALIZER_LIST is used to avoid a warning when we know that * it's safe to use 'this' in an initializer list. */ #ifdef _MSC_VER # define MOZ_THIS_IN_INITIALIZER_LIST() \
--- a/mfbt/IntegerTypeTraits.h +++ b/mfbt/IntegerTypeTraits.h @@ -74,16 +74,21 @@ struct StdintTypeForSizeAndSignedness<8, } // namespace detail template<size_t Size> struct UnsignedStdintTypeForSize : detail::StdintTypeForSizeAndSignedness<Size, false> {}; +template<size_t Size> +struct SignedStdintTypeForSize + : detail::StdintTypeForSizeAndSignedness<Size, true> +{}; + template<typename IntegerType> struct PositionOfSignBit { static_assert(IsIntegral<IntegerType>::value, "PositionOfSignBit is only for integral types"); // 8 here should be CHAR_BIT from limits.h, but the world has moved on. static const size_t value = 8 * sizeof(IntegerType) - 1; };
--- a/mfbt/RefPtr.h +++ b/mfbt/RefPtr.h @@ -280,17 +280,17 @@ public: private: void assign(T* aVal) { unref(mPtr); mPtr = aVal; } - T* mPtr; + T* MOZ_OWNING_REF mPtr; static MOZ_ALWAYS_INLINE T* ref(T* aVal) { if (aVal) { aVal->AddRef(); } return aVal; } @@ -331,17 +331,17 @@ public: T* tmp = mPtr; mPtr = nullptr; return tmp; } private: TemporaryRef(T* aVal, const DontRef&) : mPtr(aVal) {} - mutable T* mPtr; + mutable T* MOZ_OWNING_REF mPtr; TemporaryRef() MOZ_DELETE; void operator=(const TemporaryRef&) MOZ_DELETE; }; /** * OutParamRef is a wrapper that tracks a refcounted pointer passed as * an outparam argument to a function. OutParamRef implements COM T**
--- a/mfbt/WeakPtr.h +++ b/mfbt/WeakPtr.h @@ -64,16 +64,17 @@ * http://src.chromium.org/svn/trunk/src/base/memory/weak_ptr.h */ #ifndef mozilla_WeakPtr_h #define mozilla_WeakPtr_h #include "mozilla/ArrayUtils.h" #include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" #include "mozilla/NullPtr.h" #include "mozilla/RefPtr.h" #include "mozilla/TypeTraits.h" #include <string.h> namespace mozilla { @@ -117,17 +118,17 @@ public: #undef snprintf #endif private: friend class mozilla::SupportsWeakPtr<T>; void detach() { mPtr = nullptr; } - T* mPtr; + T* MOZ_NON_OWNING_REF mPtr; }; } // namespace detail template <typename T> class SupportsWeakPtr { protected:
--- a/netwerk/base/public/security-prefs.js +++ b/netwerk/base/public/security-prefs.js @@ -20,17 +20,16 @@ pref("security.ssl.enable_alpn", true); pref("security.ssl3.ecdhe_rsa_aes_128_gcm_sha256", true); pref("security.ssl3.ecdhe_ecdsa_aes_128_gcm_sha256", true); pref("security.ssl3.ecdhe_rsa_aes_128_sha", true); pref("security.ssl3.ecdhe_ecdsa_aes_128_sha", true); pref("security.ssl3.ecdhe_rsa_aes_256_sha", true); pref("security.ssl3.ecdhe_ecdsa_aes_256_sha", true); pref("security.ssl3.dhe_rsa_aes_128_sha", true); pref("security.ssl3.dhe_rsa_aes_256_sha", true); -pref("security.ssl3.dhe_dss_aes_128_sha", false); pref("security.ssl3.ecdhe_rsa_rc4_128_sha", true); pref("security.ssl3.ecdhe_ecdsa_rc4_128_sha", true); pref("security.ssl3.rsa_aes_128_sha", true); pref("security.ssl3.rsa_aes_256_sha", true); pref("security.ssl3.rsa_des_ede3_sha", true); pref("security.ssl3.rsa_rc4_128_sha", true); pref("security.ssl3.rsa_rc4_128_md5", true);
--- a/python/mozbuild/mozbuild/backend/common.py +++ b/python/mozbuild/mozbuild/backend/common.py @@ -1,26 +1,28 @@ # 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/. from __future__ import unicode_literals +import itertools import json import os import re import mozpack.path as mozpath from .base import BuildBackend from ..frontend.data import ( ConfigFileSubstitution, ExampleWebIDLInterface, HeaderFileSubstitution, + IPDLFile, GeneratedEventWebIDLFile, GeneratedWebIDLFile, PreprocessedTestWebIDLFile, PreprocessedWebIDLFile, TestManifest, TestWebIDLFile, XPIDLFile, WebIDLFile, @@ -167,16 +169,17 @@ class TestManager(object): class CommonBackend(BuildBackend): """Holds logic common to all build backends.""" def _init(self): self._idl_manager = XPIDLManager(self.environment) self._test_manager = TestManager(self.environment) self._webidls = WebIDLCollection() self._configs = set() + self._ipdl_sources = set() def consume_object(self, obj): self._configs.add(obj.config) if isinstance(obj, TestManifest): for test in obj.tests: self._test_manager.add(test, flavor=obj.flavor, topsrcdir=obj.topsrcdir) @@ -220,27 +223,48 @@ class CommonBackend(BuildBackend): elif isinstance(obj, PreprocessedWebIDLFile): self._webidls.preprocessed_sources.add(mozpath.join( obj.srcdir, obj.basename)) elif isinstance(obj, ExampleWebIDLInterface): self._webidls.example_interfaces.add(obj.name) + elif isinstance(obj, IPDLFile): + self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename)) + else: return obj.ack() def consume_finished(self): if len(self._idl_manager.idls): self._handle_idl_manager(self._idl_manager) self._handle_webidl_collection(self._webidls) + sorted_ipdl_sources = list(sorted(self._ipdl_sources)) + + def files_from(ipdl): + base = mozpath.basename(ipdl) + root, ext = mozpath.splitext(base) + + # Both .ipdl and .ipdlh become .cpp files + files = ['%s.cpp' % root] + if ext == '.ipdl': + # .ipdl also becomes Child/Parent.cpp files + files.extend(['%sChild.cpp' % root, + '%sParent.cpp' % root]) + return files + + ipdl_cppsrcs = list(itertools.chain(*[files_from(p) for p in sorted_ipdl_sources])) + + self._handle_ipdl_sources(sorted_ipdl_sources, ipdl_cppsrcs) + for config in self._configs: self.backend_input_files.add(config.source) # Write out a machine-readable file describing every test. path = mozpath.join(self.environment.topobjdir, 'all-tests.json') with self._write_file(path) as fh: s = json.dumps(self._test_manager.tests_by_path) fh.write(s)
--- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -40,17 +40,16 @@ from ..frontend.data import ( FinalTargetFiles, GeneratedInclude, GeneratedSources, HostLibrary, HostProgram, HostSimpleProgram, HostSources, InstallationTarget, - IPDLFile, JARManifest, JavaJarData, JavaScriptModules, Library, LocalInclude, PerSourceFlag, Program, Resources, @@ -272,17 +271,16 @@ class RecursiveMakeBackend(CommonBackend However, as long as there are Makefile.in files in the tree, we are tied to recursive make and thus will need this backend. """ def _init(self): CommonBackend._init(self) self._backend_files = {} - self._ipdl_sources = set() def detailed(summary): s = '{:d} total backend files; ' \ '{:d} created; {:d} updated; {:d} unchanged; ' \ '{:d} deleted; {:d} -> {:d} Makefile'.format( summary.created_count + summary.updated_count + summary.unchanged_count, summary.created_count, @@ -446,19 +444,16 @@ class RecursiveMakeBackend(CommonBackend self._process_test_harness_files(obj, backend_file) elif isinstance(obj, Resources): self._process_resources(obj, obj.resources, backend_file) elif isinstance(obj, JARManifest): backend_file.write('JAR_MANIFEST := %s\n' % obj.path) - elif isinstance(obj, IPDLFile): - self._ipdl_sources.add(mozpath.join(obj.srcdir, obj.basename)) - elif isinstance(obj, Program): self._process_program(obj.program, backend_file) self._process_linked_libraries(obj, backend_file) elif isinstance(obj, HostProgram): self._process_host_program(obj.program, backend_file) self._process_linked_libraries(obj, backend_file) @@ -745,46 +740,16 @@ class RecursiveMakeBackend(CommonBackend continue if t == b'tools' and not re.search('(?:^|\s)tools.*::', content, re.M): continue if objdir == self.environment.topobjdir: continue self._no_skip['tools'].add(mozpath.relpath(objdir, self.environment.topobjdir)) - # Write out a master list of all IPDL source files. - ipdl_dir = mozpath.join(self.environment.topobjdir, 'ipc', 'ipdl') - mk = Makefile() - - sorted_ipdl_sources = list(sorted(self._ipdl_sources)) - mk.add_statement('ALL_IPDLSRCS := %s' % ' '.join(sorted_ipdl_sources)) - - def files_from(ipdl): - base = mozpath.basename(ipdl) - root, ext = mozpath.splitext(base) - - # Both .ipdl and .ipdlh become .cpp files - files = ['%s.cpp' % root] - if ext == '.ipdl': - # .ipdl also becomes Child/Parent.cpp files - files.extend(['%sChild.cpp' % root, - '%sParent.cpp' % root]) - return files - - ipdl_cppsrcs = list(itertools.chain(*[files_from(p) for p in sorted_ipdl_sources])) - self._add_unified_build_rules(mk, ipdl_cppsrcs, ipdl_dir, - unified_prefix='UnifiedProtocols', - unified_files_makefile_variable='CPPSRCS') - - mk.add_statement('IPDLDIRS := %s' % ' '.join(sorted(set(mozpath.dirname(p) - for p in self._ipdl_sources)))) - - with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls: - mk.dump(ipdls, removal_guard=False) - self._fill_root_mk() # Write out a dependency file used to determine whether a config.status # re-run is needed. inputs = sorted(p.replace(os.sep, '/') for p in self.backend_input_files) # We need to use $(DEPTH) so the target here matches what's in # rules.mk. If they are different, the dependencies don't get pulled in @@ -1327,16 +1292,33 @@ INSTALL_TARGETS += %(prefix)s # that if the Makefile.in disappears, this will force # moz.build traversal. This means that when we remove empty # Makefile.in files, the old file will get replaced with # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) self.summary.makefile_out_count += 1 + def _handle_ipdl_sources(self, sorted_ipdl_sources, ipdl_cppsrcs): + # Write out a master list of all IPDL source files. + ipdl_dir = mozpath.join(self.environment.topobjdir, 'ipc', 'ipdl') + mk = Makefile() + + mk.add_statement('ALL_IPDLSRCS := %s' % ' '.join(sorted_ipdl_sources)) + + self._add_unified_build_rules(mk, ipdl_cppsrcs, ipdl_dir, + unified_prefix='UnifiedProtocols', + unified_files_makefile_variable='CPPSRCS') + + mk.add_statement('IPDLDIRS := %s' % ' '.join(sorted(set(mozpath.dirname(p) + for p in self._ipdl_sources)))) + + with self._write_file(mozpath.join(ipdl_dir, 'ipdlsrcs.mk')) as ipdls: + mk.dump(ipdls, removal_guard=False) + def _handle_webidl_collection(self, webidls): if not webidls.all_stems(): return bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', 'bindings') all_inputs = set(webidls.all_static_sources())
--- a/python/mozbuild/mozbuild/backend/visualstudio.py +++ b/python/mozbuild/mozbuild/backend/visualstudio.py @@ -96,26 +96,26 @@ class VisualStudioBackend(CommonBackend) obj.ack() reldir = getattr(obj, 'relativedir', None) if hasattr(obj, 'config') and reldir not in self._paths_to_configs: self._paths_to_configs[reldir] = obj.config if isinstance(obj, Sources): - self._add_sources(self, reldir, obj) + self._add_sources(reldir, obj) elif isinstance(obj, HostSources): - self._add_sources(self, reldir, obj) + self._add_sources(reldir, obj) elif isinstance(obj, GeneratedSources): - self._add_sources(self, reldir, obj) + self._add_sources(reldir, obj) elif isinstance(obj, UnifiedSources): - self._addr_sources(self, reldir, obj) + self._add_sources(reldir, obj) elif isinstance(obj, Library): self._libs_to_paths[obj.basename] = reldir elif isinstance(obj, Defines): self._paths_to_defines.setdefault(reldir, {}).update(obj.defines) elif isinstance(obj, LocalInclude):
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp +++ b/security/manager/ssl/src/SSLServerCertVerification.cpp @@ -1104,17 +1104,17 @@ AuthCertificate(CertVerifier& certVerifi // We want to remember the CA certs in the temp db, so that the application can find the // complete chain at any time it might need it. // But we keep only those CA certs in the temp db, that we didn't already know. RefPtr<nsSSLStatus> status(infoObject->SSLStatus()); RefPtr<nsNSSCertificate> nsc; - if (!status || !status->mServerCert) { + if (!status || !status->HasServerCert()) { if( rv == SECSuccess ){ nsc = nsNSSCertificate::Create(cert, &evOidPolicy); } else { nsc = nsNSSCertificate::Create(cert); } } @@ -1136,20 +1136,27 @@ AuthCertificate(CertVerifier& certVerifi nullptr, rv); } else { // Certificate verification failed, update the status' bits. RememberCertErrorsTable::GetInstance().LookupCertErrorBits( infoObject, status); } - if (status && !status->mServerCert) { - status->mServerCert = nsc; + if (status && !status->HasServerCert()) { + nsNSSCertificate::EVStatus evStatus; + if (evOidPolicy == SEC_OID_UNKNOWN || rv != SECSuccess) { + evStatus = nsNSSCertificate::ev_status_invalid; + } else { + evStatus = nsNSSCertificate::ev_status_valid; + } + + status->SetServerCert(nsc, evStatus); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, - ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get())); + ("AuthCertificate setting NEW cert %p\n", nsc.get())); } } if (rv != SECSuccess) { // Certificate validation failed; store the peer certificate chain on // infoObject so it can be used for error reporting. Note: infoObject // indirectly takes ownership of peerCertChain. infoObject->SetFailedCertChain(peerCertChain);
--- a/security/manager/ssl/src/TransportSecurityInfo.cpp +++ b/security/manager/ssl/src/TransportSecurityInfo.cpp @@ -237,21 +237,21 @@ TransportSecurityInfo::formatErrorMessag if (errorCode == 0) { result.Truncate(); return NS_OK; } nsresult rv; NS_ConvertASCIItoUTF16 hostNameU(mHostName); NS_ASSERTION(errorMessageType != OverridableCertErrorMessage || - (mSSLStatus && mSSLStatus->mServerCert && + (mSSLStatus && mSSLStatus->HasServerCert() && mSSLStatus->mHaveCertErrorBits), "GetErrorLogMessage called for cert error without cert"); if (errorMessageType == OverridableCertErrorMessage && - mSSLStatus && mSSLStatus->mServerCert) { + mSSLStatus && mSSLStatus->HasServerCert()) { rv = formatOverridableCertErrorMessage(*mSSLStatus, errorCode, mHostName, mPort, suppressPort443, wantsHtml, result); } else { rv = formatPlainErrorMessage(mHostName, mPort, errorCode, @@ -291,18 +291,18 @@ TransportSecurityInfo::GetInterface(cons } return rv; } // This is a new magic value. However, it re-uses the first 4 bytes // of the previous value. This is so when older versions attempt to // read a newer serialized TransportSecurityInfo, they will actually // fail and return NS_ERROR_FAILURE instead of silently failing. -#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x2429, 0x4866, \ - { 0x92, 0x89, 0x45, 0x51, 0xc2, 0x01, 0xca, 0xf2 } } +#define TRANSPORTSECURITYINFOMAGIC { 0xa9863a23, 0x328f, 0x45ab, \ + { 0xa8, 0xa4, 0x35, 0x18, 0x80, 0x04, 0x77, 0x8d } } static NS_DEFINE_CID(kTransportSecurityInfoMagic, TRANSPORTSECURITYINFOMAGIC); NS_IMETHODIMP TransportSecurityInfo::Write(nsIObjectOutputStream* stream) { nsresult rv = stream->WriteID(kTransportSecurityInfoMagic); if (NS_FAILED(rv)) { return rv; @@ -1084,17 +1084,17 @@ void TransportSecurityInfo::SetStatusErrorBits(nsIX509Cert & cert, uint32_t collected_errors) { MutexAutoLock lock(mMutex); if (!mSSLStatus) mSSLStatus = new nsSSLStatus(); - mSSLStatus->mServerCert = &cert; + mSSLStatus->SetServerCert(&cert, nsNSSCertificate::ev_status_invalid); mSSLStatus->mHaveCertErrorBits = true; mSSLStatus->mIsDomainMismatch = collected_errors & nsICertOverrideService::ERROR_MISMATCH; mSSLStatus->mIsNotValidAtThisTime = collected_errors & nsICertOverrideService::ERROR_TIME; mSSLStatus->mIsUntrusted = collected_errors & nsICertOverrideService::ERROR_UNTRUSTED;
--- a/security/manager/ssl/src/nsNSSCallbacks.cpp +++ b/security/manager/ssl/src/nsNSSCallbacks.cpp @@ -1283,25 +1283,25 @@ void HandshakeCallback(PRFileDesc* fd, v if (NS_FAILED(rv)) { equals_previous = false; } } if (equals_previous) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using PREV cert %p\n", prevcert.get())); - status->mServerCert = prevcert; + status->SetServerCert(prevcert, nsNSSCertificate::ev_status_unknown); } else { - if (status->mServerCert) { + if (status->HasServerCert()) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, - ("HandshakeCallback KEEPING cert %p\n", status->mServerCert.get())); + ("HandshakeCallback KEEPING existing cert\n")); } else { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("HandshakeCallback using NEW cert %p\n", nssc.get())); - status->mServerCert = nssc; + status->SetServerCert(nssc, nsNSSCertificate::ev_status_unknown); } } infoObject->NoteTimeUntilReady(); infoObject->SetHandshakeCompleted(); }
--- a/security/manager/ssl/src/nsNSSCertificate.h +++ b/security/manager/ssl/src/nsNSSCertificate.h @@ -44,35 +44,39 @@ public: nsNSSCertificate(); nsresult FormatUIStrings(const nsAutoString& nickname, nsAutoString& nickWithSerial, nsAutoString& details); static nsNSSCertificate* Create(CERTCertificate*cert = nullptr, SECOidTag* evOidPolicy = nullptr); static nsNSSCertificate* ConstructFromDER(char* certDER, int derLen); + enum EVStatus { + ev_status_invalid = 0, + ev_status_valid = 1, + ev_status_unknown = 2 + }; + private: virtual ~nsNSSCertificate(); mozilla::ScopedCERTCertificate mCert; bool mPermDelete; uint32_t mCertType; nsresult CreateASN1Struct(nsIASN1Object** aRetVal); nsresult CreateTBSCertificateASN1Struct(nsIASN1Sequence** retSequence, nsINSSComponent* nssComponent); nsresult GetSortableDate(PRTime aTime, nsAString& _aSortableDate); virtual void virtualDestroyNSSReference(); void destructorSafeDestroyNSSReference(); bool InitFromDER(char* certDER, int derLen); // return false on failure nsresult GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg); - enum { - ev_status_invalid = 0, ev_status_valid = 1, ev_status_unknown = 2 - } mCachedEVStatus; + EVStatus mCachedEVStatus; SECOidTag mCachedEVOidTag; nsresult hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV); nsresult getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV); }; namespace mozilla { SECStatus ConstructCERTCertListFromReversedDERArray(
--- a/security/manager/ssl/src/nsNSSComponent.cpp +++ b/security/manager/ssl/src/nsNSSComponent.cpp @@ -645,19 +645,16 @@ static const CipherPref sCipherPrefs[] = TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, true }, { "security.ssl3.dhe_rsa_aes_128_sha", TLS_DHE_RSA_WITH_AES_128_CBC_SHA, true }, { "security.ssl3.dhe_rsa_aes_256_sha", TLS_DHE_RSA_WITH_AES_256_CBC_SHA, true }, - { "security.ssl3.dhe_dss_aes_128_sha", - TLS_DHE_DSS_WITH_AES_128_CBC_SHA, false }, // deprecated (DSS) - { "security.ssl3.ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4) { "security.ssl3.ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, true, true }, // deprecated (RC4) { "security.ssl3.rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA, true }, // deprecated (RSA key exchange) { "security.ssl3.rsa_aes_256_sha",
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -393,18 +393,19 @@ nsNSSSocketInfo::IsAcceptableForHost(con // need to be considered. They are joinable. if (hostname.Equals(GetHostName())) { *_retval = true; return NS_OK; } // Before checking the server certificate we need to make sure the // handshake has completed. - if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->mServerCert) + if (!mHandshakeCompleted || !SSLStatus() || !SSLStatus()->HasServerCert()) { return NS_OK; + } // If the cert has error bits (e.g. it is untrusted) then do not join. // The value of mHaveCertErrorBits is only reliable because we know that // the handshake completed. if (SSLStatus()->mHaveCertErrorBits) return NS_OK; // If the connection is using client certificates then do not join @@ -413,17 +414,20 @@ nsNSSSocketInfo::IsAcceptableForHost(con if (mSentClientCert) return NS_OK; // Ensure that the server certificate covers the hostname that would // like to join this connection ScopedCERTCertificate nssCert; - nsCOMPtr<nsIX509Cert> cert(SSLStatus()->mServerCert); + nsCOMPtr<nsIX509Cert> cert; + if (NS_FAILED(SSLStatus()->GetServerCert(getter_AddRefs(cert)))) { + return NS_OK; + } if (cert) { nssCert = cert->GetCert(); } if (!nssCert) { return NS_OK; }
--- a/security/manager/ssl/src/nsSSLStatus.cpp +++ b/security/manager/ssl/src/nsSSLStatus.cpp @@ -116,38 +116,30 @@ nsSSLStatus::GetIsUntrusted(bool* aIsUnt } NS_IMETHODIMP nsSSLStatus::GetIsExtendedValidation(bool* aIsEV) { NS_ENSURE_ARG_POINTER(aIsEV); *aIsEV = false; -#ifdef MOZ_NO_EV_CERTS - return NS_OK; -#else - nsCOMPtr<nsIX509Cert> cert = mServerCert; - // mServerCert should never be null when this method is called because - // nsSSLStatus objects always have mServerCert set right after they are - // constructed and before they are returned. GetIsExtendedValidation should - // only be called in the chrome process (in e10s), and mServerCert will always - // implement nsIIdentityInfo in the chrome process. - nsCOMPtr<nsIIdentityInfo> idinfo = do_QueryInterface(cert); - if (!idinfo) { - NS_ERROR("nsSSLStatus has null mServerCert or was called in the content " - "process"); - return NS_ERROR_UNEXPECTED; - } - // Never allow bad certs for EV, regardless of overrides. if (mHaveCertErrorBits) { return NS_OK; } - return idinfo->GetIsExtendedValidation(aIsEV); + if (mHasIsEVStatus) { + *aIsEV = mIsEV; + return NS_OK; + } + +#ifdef MOZ_NO_EV_CERTS + return NS_OK; +#else + return NS_ERROR_NOT_AVAILABLE; #endif } NS_IMETHODIMP nsSSLStatus::Read(nsIObjectInputStream* aStream) { nsCOMPtr<nsISupports> cert; nsresult rv = aStream->ReadObject(true, getter_AddRefs(cert)); @@ -164,17 +156,21 @@ nsSSLStatus::Read(nsIObjectInputStream* NS_ENSURE_SUCCESS(rv, rv); rv = aStream->ReadBoolean(&mIsDomainMismatch); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->ReadBoolean(&mIsNotValidAtThisTime); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->ReadBoolean(&mIsUntrusted); NS_ENSURE_SUCCESS(rv, rv); + rv = aStream->ReadBoolean(&mIsEV); + NS_ENSURE_SUCCESS(rv, rv); + rv = aStream->ReadBoolean(&mHasIsEVStatus); + NS_ENSURE_SUCCESS(rv, rv); rv = aStream->ReadBoolean(&mHaveCipherSuiteAndProtocol); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->ReadBoolean(&mHaveCertErrorBits); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -192,17 +188,21 @@ nsSSLStatus::Write(nsIObjectOutputStream NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteBoolean(mIsDomainMismatch); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteBoolean(mIsNotValidAtThisTime); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteBoolean(mIsUntrusted); NS_ENSURE_SUCCESS(rv, rv); + rv = aStream->WriteBoolean(mIsEV); + NS_ENSURE_SUCCESS(rv, rv); + rv = aStream->WriteBoolean(mHasIsEVStatus); + NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteBoolean(mHaveCipherSuiteAndProtocol); NS_ENSURE_SUCCESS(rv, rv); rv = aStream->WriteBoolean(mHaveCertErrorBits); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -269,18 +269,43 @@ nsSSLStatus::GetClassIDNoAlloc(nsCID* aC } nsSSLStatus::nsSSLStatus() : mCipherSuite(0) , mProtocolVersion(0) , mIsDomainMismatch(false) , mIsNotValidAtThisTime(false) , mIsUntrusted(false) +, mIsEV(false) +, mHasIsEVStatus(false) , mHaveCipherSuiteAndProtocol(false) , mHaveCertErrorBits(false) { } NS_IMPL_ISUPPORTS(nsSSLStatus, nsISSLStatus, nsISerializable, nsIClassInfo) nsSSLStatus::~nsSSLStatus() { } + +void +nsSSLStatus::SetServerCert(nsIX509Cert* aServerCert, nsNSSCertificate::EVStatus aEVStatus) +{ + mServerCert = aServerCert; + + if (aEVStatus != nsNSSCertificate::ev_status_unknown) { + mIsEV = (aEVStatus == nsNSSCertificate::ev_status_valid); + mHasIsEVStatus = true; + return; + } + +#ifndef MOZ_NO_EV_CERTS + nsCOMPtr<nsIIdentityInfo> idinfo = do_QueryInterface(mServerCert); + if (idinfo) { + nsresult rv = idinfo->GetIsExtendedValidation(&mIsEV); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + mHasIsEVStatus = true; + } +#endif +}
--- a/security/manager/ssl/src/nsSSLStatus.h +++ b/security/manager/ssl/src/nsSSLStatus.h @@ -8,46 +8,56 @@ #define _NSSSLSTATUS_H #include "nsISSLStatus.h" #include "nsCOMPtr.h" #include "nsXPIDLString.h" #include "nsIX509Cert.h" #include "nsISerializable.h" #include "nsIClassInfo.h" +#include "nsNSSCertificate.h" // For EVStatus class nsSSLStatus MOZ_FINAL : public nsISSLStatus , public nsISerializable , public nsIClassInfo { protected: virtual ~nsSSLStatus(); public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSISSLSTATUS NS_DECL_NSISERIALIZABLE NS_DECL_NSICLASSINFO nsSSLStatus(); + void SetServerCert(nsIX509Cert* aServerCert, nsNSSCertificate::EVStatus aEVStatus); + + bool HasServerCert() { + return mServerCert != nullptr; + } + /* public for initilization in this file */ - nsCOMPtr<nsIX509Cert> mServerCert; - uint16_t mCipherSuite; uint16_t mProtocolVersion; bool mIsDomainMismatch; bool mIsNotValidAtThisTime; bool mIsUntrusted; + bool mIsEV; + bool mHasIsEVStatus; bool mHaveCipherSuiteAndProtocol; /* mHaveCertErrrorBits is relied on to determine whether or not a SPDY connection is eligible for joining in nsNSSSocketInfo::JoinConnection() */ bool mHaveCertErrorBits; + +private: + nsCOMPtr<nsIX509Cert> mServerCert; }; #define NS_SSLSTATUS_CID \ -{ 0x61f69c85, 0x0fed, 0x44fb, \ - { 0x89, 0x8f, 0xa4, 0xb1, 0x3c, 0x33, 0x3c, 0x8d } } +{ 0xe2f14826, 0x9e70, 0x4647, \ + { 0xb2, 0x3f, 0x10, 0x10, 0xf5, 0x12, 0x46, 0x28 } } #endif
--- a/security/pkix/include/pkix/ScopedPtr.h +++ b/security/pkix/include/pkix/ScopedPtr.h @@ -26,17 +26,17 @@ #define mozilla_pkix__ScopedPtr_h #include "pkix/nullptr.h" namespace mozilla { namespace pkix { // Similar to boost::scoped_ptr and std::unique_ptr. Does not support copying // or assignment. -template <typename T, void (*Destroyer)(T*)> +template <typename T, void (&Destroyer)(T*)> class ScopedPtr { public: explicit ScopedPtr(T* value = nullptr) : mValue(value) { } ~ScopedPtr() { if (mValue) { Destroyer(mValue); @@ -68,38 +68,38 @@ public: protected: T* mValue; ScopedPtr(const ScopedPtr&) /* = delete */; void operator=(const ScopedPtr&) /* = delete */; }; -template <typename T, void(*Destroyer)(T*)> +template <typename T, void(&Destroyer)(T*)> inline bool operator==(T* a, const ScopedPtr<T, Destroyer>& b) { return a == b.get(); } -template <typename T, void(*Destroyer)(T*)> +template <typename T, void(&Destroyer)(T*)> inline bool operator==(const ScopedPtr<T, Destroyer>& a, T* b) { return a.get() == b; } -template <typename T, void(*Destroyer)(T*)> +template <typename T, void(&Destroyer)(T*)> inline bool operator!=(T* a, const ScopedPtr<T, Destroyer>& b) { return a != b.get(); } -template <typename T, void(*Destroyer)(T*)> +template <typename T, void(&Destroyer)(T*)> inline bool operator!=(const ScopedPtr<T, Destroyer>& a, T* b) { return a.get() != b; } } } // namespace mozilla::pkix
--- a/security/pkix/include/pkix/bind.h +++ b/security/pkix/include/pkix/bind.h @@ -69,152 +69,161 @@ template <typename V> V& ref(V& v template <typename V> const V& cref(const V& v) { return v; } namespace internal { template <typename R, typename P1, typename B1> class Bind1 { public: - typedef R (*F)(P1 &, B1 &); - Bind1(F f, B1 & b1) : f(f), b1(b1) { } - R operator()(P1 & p1) const { return f(p1, b1); } + typedef R (&F)(P1&, B1&); + Bind1(F f, B1& b1) : f(f), b1(b1) { } + R operator()(P1& p1) const { return f(p1, b1); } private: - const F f; + F f; B1& b1; void operator=(const Bind1&) /*= delete*/; }; template <typename R, typename P1, typename B1, typename B2> class Bind2 { public: - typedef R (*F)(P1&, B1&, B2&); + typedef R (&F)(P1&, B1&, B2&); Bind2(F f, B1& b1, B2& b2) : f(f), b1(b1), b2(b2) { } R operator()(P1& p1) const { return f(p1, b1, b2); } private: - const F f; + F f; B1& b1; B2& b2; void operator=(const Bind2&) /*= delete*/; }; template <typename R, typename P1, typename B1, typename B2, typename B3> class Bind3 { public: - typedef R (*F)(P1&, B1, B2, B3&); + typedef R (&F)(P1&, B1, B2, B3&); Bind3(F f, B1& b1, B2& b2, B3& b3) : f(f), b1(b1), b2(b2), b3(b3) { } R operator()(P1& p1) const { return f(p1, b1, b2, b3); } private: - const F f; + F f; B1& b1; B2& b2; B3& b3; void operator=(const Bind3&) /*= delete*/; }; template <typename R, typename P1, typename B1, typename B2, typename B3, typename B4> class Bind4 { public: - typedef R (*F)(P1&, B1, B2, B3&, B4&); + typedef R (&F)(P1&, B1, B2, B3&, B4&); Bind4(F f, B1& b1, B2& b2, B3& b3, B4& b4) : f(f), b1(b1), b2(b2), b3(b3), b4(b4) { } R operator()(P1& p1) const { return f(p1, b1, b2, b3, b4); } private: - const F f; + F f; B1& b1; B2& b2; B3& b3; B4& b4; void operator=(const Bind4&) /*= delete*/; }; template <typename R, typename C1, typename P1, typename P2, typename P3, typename P4> class BindToMemberFunction4 { public: + // XXX: C++ doesn't have reference-to-member function, only + // pointer-to-member function, so we can't enforce the non-nullness of f + // using the type system. typedef R (C1::*F)(P1&, P2&, P3, P4&); - BindToMemberFunction4(F f, C1* that) : f(f), that(that) { } - R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const { return (that->*f)(p1, p2, p3, p4); } + BindToMemberFunction4(F f, C1& that) : f(f), that(that) { } + R operator()(P1& p1, P2& p2, P3 p3, P4& p4) const + { + return (that.*f)(p1, p2, p3, p4); + } private: const F f; - C1* const that; + C1& that; void operator=(const BindToMemberFunction4&) /*= delete*/; }; template <typename R, typename P1, typename B1, typename B2, typename B3, typename B4, typename B5> class Bind5 { public: - typedef R (*F)(P1&, B1, B2, B3, B4, B5); + typedef R (&F)(P1&, B1, B2, B3, B4, B5); Bind5(F f, B1 b1, B2 b2, B3 b3, B4 b4, B5 b5) : f(f), b1(b1), b2(b2), b3(b3), b4(b4), b5(b5) { } R operator()(P1& p1) const { return f(p1, b1, b2, b3, b4, b5); } private: - const F f; + F f; B1 b1; B2 b2; B3 b3; B4 b4; B5 b5; void operator=(const Bind5&) /*= delete*/; }; } // namespace internal template <typename R, typename P1, typename B1> inline internal::Bind1<R, P1, B1> -bind(R (*f)(P1&, B1&), Placeholder1&, B1& b1) +bind(R (&f)(P1&, B1&), Placeholder1&, B1& b1) { return internal::Bind1<R, P1, B1>(f, b1); } template <typename R, typename P1, typename B1, typename B2> inline internal::Bind2<R, P1, B1, B2> -bind(R (*f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2) +bind(R (&f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2) { return internal::Bind2<R, P1, B1, B2>(f, b1, b2); } template <typename R, typename P1, typename B1, typename B2, typename B3> inline internal::Bind3<R, P1, const B1, const B2, B3> -bind(R (*f)(P1&, B1, B2, B3&), Placeholder1&, const B1& b1, const B2& b2, +bind(R (&f)(P1&, B1, B2, B3&), Placeholder1&, const B1& b1, const B2& b2, B3& b3) { return internal::Bind3<R, P1, const B1, const B2, B3>(f, b1, b2, b3); } template <typename R, typename P1, typename B1, typename B2, typename B3, typename B4> inline internal::Bind4<R, P1, const B1, const B2, B3, B4> -bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2, +bind(R (&f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2, B3& b3, B4& b4) { return internal::Bind4<R, P1, const B1, const B2, B3, B4>(f, b1, b2, b3, b4); } +// XXX: C++ doesn't have reference-to-member function, only +// pointer-to-member function, so we can't enforce the non-nullness of f +// using the type system. template <typename R, typename C1, typename P1, typename P2, typename P3, typename P4> inline internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4> -bind(R (C1::*f)(P1&, P2&, P3, P4&), C1* that, Placeholder1&, Placeholder2&, - Placeholder3, Placeholder4&) +bind(R (C1::*f)(P1&, P2&, P3, P4&), C1& that, Placeholder1&, + Placeholder2&, Placeholder3, Placeholder4&) { return internal::BindToMemberFunction4<R, C1, P1, P2, P3, P4>(f, that); } template <typename R, typename P1, typename B1, typename B2, typename B3, typename B4, typename B5> inline internal::Bind5<R, P1, B1, B2, B3, B4, B5&> -bind(R (*f)(P1&, B1, B2, B3, B4, B5&), Placeholder1&, B1 b1, B2 b2, B3 b3, +bind(R (&f)(P1&, B1, B2, B3, B4, B5&), Placeholder1&, B1 b1, B2 b2, B3 b3, B4 b4, B5& b5) { return internal::Bind5<R, P1, B1, B2, B3, B4, B5&>(f, b1, b2, b3, b4, b5); } #endif } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixcert.cpp +++ b/security/pkix/lib/pkixcert.cpp @@ -136,17 +136,17 @@ BackCert::Init() if (tbsCertificate.Peek(CSC | 2)) { rv = der::ExpectTagAndSkipValue(tbsCertificate, CSC | 2); if (rv != Success) { return rv; } } rv = der::OptionalExtensions(tbsCertificate, CSC | 3, - bind(&BackCert::RememberExtension, this, _1, + bind(&BackCert::RememberExtension, *this, _1, _2, _3, _4)); if (rv != Success) { return rv; } // The Netscape Certificate Type extension is an obsolete // Netscape-proprietary mechanism that we ignore in favor of the standard // extensions. However, some CAs have issued certificates with the Netscape
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp +++ b/security/pkix/test/gtest/pkixbuild_tests.cpp @@ -17,35 +17,31 @@ * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ +#include <map> #include "cert.h" -#include "nss.h" #include "pkix/pkix.h" -#include "pkix/pkixnss.h" #include "pkixgtest.h" #include "pkixtestutil.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; -typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate> - ScopedCERTCertificate; -typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList; - static ByteString CreateCert(const char* issuerCN, const char* subjectCN, EndEntityOrCA endEntityOrCA, - /*out*/ ScopedCERTCertificate* subjectCert = nullptr) + /*optional modified*/ std::map<ByteString, ByteString>* + subjectDERToCertDER = nullptr) { static long serialNumberValue = 0; ++serialNumberValue; ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue)); EXPECT_FALSE(ENCODING_FAILED(serialNumber)); ByteString issuerDER(CNToDERName(issuerCN)); ByteString subjectDER(CNToDERName(subjectCN)); @@ -60,101 +56,83 @@ CreateCert(const char* issuerCN, ScopedTestKeyPair reusedKey(CloneReusedKeyPair()); ByteString certDER(CreateEncodedCertificate( v3, sha256WithRSAEncryption, serialNumber, issuerDER, oneDayBeforeNow, oneDayAfterNow, subjectDER, *reusedKey, extensions, *reusedKey, sha256WithRSAEncryption)); EXPECT_FALSE(ENCODING_FAILED(certDER)); - if (subjectCert) { - SECItem certDERItem = { - siBuffer, - const_cast<uint8_t*>(certDER.data()), - static_cast<unsigned int>(certDER.length()) - }; - *subjectCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), - &certDERItem, nullptr, false, true); - EXPECT_TRUE(*subjectCert); + + if (subjectDERToCertDER) { + (*subjectDERToCertDER)[subjectDER] = certDER; } + return certDER; } class TestTrustDomain : public TrustDomain { public: // The "cert chain tail" is a longish chain of certificates that is used by // all of the tests here. We share this chain across all the tests in order // to speed up the tests (generating keypairs for the certs is very slow). bool SetUpCertChainTail() { static char const* const names[] = { "CA1 (Root)", "CA2", "CA3", "CA4", "CA5", "CA6", "CA7" }; - static_assert(MOZILLA_PKIX_ARRAY_LENGTH(names) == - MOZILLA_PKIX_ARRAY_LENGTH(certChainTail), - "mismatch in sizes of names and certChainTail arrays"); - for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) { const char* issuerName = i == 0 ? names[0] : names[i-1]; - (void) CreateCert(issuerName, names[i], EndEntityOrCA::MustBeCA, - &certChainTail[i]); + CreateCACert(issuerName, names[i]); + if (i == 0) { + rootCACertDER = leafCACertDER; + } } return true; } + void CreateCACert(const char* issuerName, const char* subjectName) + { + leafCACertDER = CreateCert(issuerName, subjectName, + EndEntityOrCA::MustBeCA, &subjectDERToCertDER); + assert(!ENCODING_FAILED(leafCACertDER)); + } + + ByteString GetLeafCACertDER() const { return leafCACertDER; } + private: virtual Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert, /*out*/ TrustLevel& trustLevel) { - Input rootDER; - Result rv = rootDER.Init(certChainTail[0]->derCert.data, - certChainTail[0]->derCert.len); - EXPECT_EQ(Success, rv); - if (InputsAreEqual(candidateCert, rootDER)) { - trustLevel = TrustLevel::TrustAnchor; - } else { - trustLevel = TrustLevel::InheritsTrust; - } + trustLevel = InputEqualsByteString(candidateCert, rootCACertDER) + ? TrustLevel::TrustAnchor + : TrustLevel::InheritsTrust; return Success; } virtual Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time time) { - SECItem encodedIssuerNameSECItem = - UnsafeMapInputToSECItem(encodedIssuerName); - ScopedCERTCertList - candidates(CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(), - &encodedIssuerNameSECItem, 0, - false)); - if (candidates) { - for (CERTCertListNode* n = CERT_LIST_HEAD(candidates); - !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) { - bool keepGoing; - Input derCert; - Result rv = derCert.Init(n->cert->derCert.data, n->cert->derCert.len); - EXPECT_EQ(Success, rv); - if (rv != Success) { - return rv; - } - rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/, - keepGoing); - if (rv != Success) { - return rv; - } - if (!keepGoing) { - break; - } - } + ByteString subjectDER(InputToByteString(encodedIssuerName)); + ByteString certDER(subjectDERToCertDER[subjectDER]); + Input derCert; + Result rv = derCert.Init(certDER.data(), certDER.length()); + if (rv != Success) { + return rv; } - + bool keepGoing; + rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/, + keepGoing); + if (rv != Success) { + return rv; + } return Success; } virtual Result CheckRevocation(EndEntityOrCA, const CertID&, Time, /*optional*/ const Input*, /*optional*/ const Input*) { return Success; @@ -163,83 +141,69 @@ private: virtual Result IsChainValid(const DERArray&, Time) { return Success; } virtual Result VerifySignedData(const SignedDataWithSignature& signedData, Input subjectPublicKeyInfo) { - return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, - MINIMUM_TEST_KEY_BITS, nullptr); + return TestVerifySignedData(signedData, subjectPublicKeyInfo); } virtual Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf, size_t digestBufLen) { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; } virtual Result CheckPublicKey(Input subjectPublicKeyInfo) { return TestCheckPublicKey(subjectPublicKeyInfo); } - // We hold references to CERTCertificates in the cert chain tail so that we - // CERT_CreateSubjectCertList can find them. - ScopedCERTCertificate certChainTail[7]; - -public: - CERTCertificate* GetLeafCACert() const - { - return certChainTail[MOZILLA_PKIX_ARRAY_LENGTH(certChainTail) - 1].get(); - } + std::map<ByteString, ByteString> subjectDERToCertDER; + ByteString leafCACertDER; + ByteString rootCACertDER; }; class pkixbuild : public ::testing::Test { public: static void SetUpTestCase() { - // XXX(Bug 1070444): We have to initialize NSS explicitly for these tests, - // unlike other tests, because we're using NSS directly. - if (NSS_NoDB_Init(nullptr) != SECSuccess) { - abort(); - } - if (!trustDomain.SetUpCertChainTail()) { abort(); } } protected: static TestTrustDomain trustDomain; }; /*static*/ TestTrustDomain pkixbuild::trustDomain; TEST_F(pkixbuild, MaxAcceptableCertChainLength) { { + ByteString leafCACert(trustDomain.GetLeafCACertDER()); Input certDER; - ASSERT_EQ(Success, certDER.Init(trustDomain.GetLeafCACert()->derCert.data, - trustDomain.GetLeafCACert()->derCert.len)); + ASSERT_EQ(Success, certDER.Init(leafCACert.data(), leafCACert.length())); ASSERT_EQ(Success, BuildCertChain(trustDomain, certDER, Now(), EndEntityOrCA::MustBeCA, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, nullptr/*stapledOCSPResponse*/)); } { - ScopedCERTCertificate cert; ByteString certDER(CreateCert("CA7", "Direct End-Entity", EndEntityOrCA::MustBeEndEntity)); ASSERT_FALSE(ENCODING_FAILED(certDER)); Input certDERInput; ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length())); ASSERT_EQ(Success, BuildCertChain(trustDomain, certDERInput, Now(), EndEntityOrCA::MustBeEndEntity, @@ -249,24 +213,20 @@ TEST_F(pkixbuild, MaxAcceptableCertChain nullptr/*stapledOCSPResponse*/)); } } TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength) { static char const* const caCertName = "CA Too Far"; - // We need a CERTCertificate for caCert so that the trustdomain's FindIssuer - // method can find it through the NSS cert DB. - ScopedCERTCertificate caCert; + trustDomain.CreateCACert("CA7", caCertName); { - ByteString certDER(CreateCert("CA7", caCertName, EndEntityOrCA::MustBeCA, - &caCert)); - ASSERT_FALSE(ENCODING_FAILED(certDER)); + ByteString certDER(trustDomain.GetLeafCACertDER()); Input certDERInput; ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length())); ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER, BuildCertChain(trustDomain, certDERInput, Now(), EndEntityOrCA::MustBeCA, KeyUsage::noParticularKeyUsageRequired, KeyPurposeId::id_kp_serverAuth, CertPolicyId::anyPolicy, @@ -346,18 +306,17 @@ public: virtual Result IsChainValid(const DERArray&, Time) { return Success; } virtual Result VerifySignedData(const SignedDataWithSignature& signedData, Input subjectPublicKeyInfo) { - return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, - MINIMUM_TEST_KEY_BITS, nullptr); + return TestVerifySignedData(signedData, subjectPublicKeyInfo); } virtual Result DigestBuf(Input, /*out*/uint8_t*, size_t) { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; }
--- a/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp +++ b/security/pkix/test/gtest/pkixcert_signature_algorithm_tests.cpp @@ -1,15 +1,14 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ #include "pkix/pkix.h" -#include "pkix/pkixnss.h" #include "pkixgtest.h" #include "pkixtestutil.h" using namespace mozilla::pkix; using namespace mozilla::pkix::test; static ByteString CreateCert(const char* issuerCN, @@ -105,18 +104,17 @@ private: { return Success; } virtual Result VerifySignedData(const SignedDataWithSignature& signedData, Input subjectPublicKeyInfo) { EXPECT_NE(SignatureAlgorithm::unsupported_algorithm, signedData.algorithm); - return ::mozilla::pkix::VerifySignedData(signedData, subjectPublicKeyInfo, - MINIMUM_TEST_KEY_BITS, nullptr); + return TestVerifySignedData(signedData, subjectPublicKeyInfo); } virtual Result DigestBuf(Input, uint8_t*, size_t) { ADD_FAILURE(); return Result::FATAL_ERROR_LIBRARY_FAILURE; }
--- a/security/pkix/test/lib/pkixtestutil.cpp +++ b/security/pkix/test/lib/pkixtestutil.cpp @@ -78,16 +78,30 @@ InputEqualsByteString(Input input, const // Init can only fail if it is given a bad pointer or if the input is too // long, which won't ever happen. Plus, if it does, it is ok to call abort // since this is only test code. abort(); } return InputsAreEqual(input, bsInput); } +ByteString +InputToByteString(Input input) +{ + ByteString result; + Reader reader(input); + for (;;) { + uint8_t b; + if (reader.Read(b) != Success) { + return result; + } + result.push_back(b); + } +} + Result TamperOnce(/*in/out*/ ByteString& item, const ByteString& from, const ByteString& to) { if (from.length() < 8) { return Result::FATAL_ERROR_INVALID_ARGS; } if (from.length() != to.length()) {
--- a/security/pkix/test/lib/pkixtestutil.h +++ b/security/pkix/test/lib/pkixtestutil.h @@ -62,16 +62,17 @@ public: template <size_t N> explicit TestInput(const char (&valueString)[N]) : Input(reinterpret_cast<const uint8_t(&)[N-1]>(valueString)) { } }; bool InputEqualsByteString(Input input, const ByteString& bs); +ByteString InputToByteString(Input input); // python DottedOIDToCode.py --tlv id-kp-OCSPSigning 1.3.6.1.5.5.7.3.9 static const uint8_t tlv_id_kp_OCSPSigning[] = { 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09 }; // python DottedOIDToCode.py --tlv id-kp-serverAuth 1.3.6.1.5.5.7.3.1 static const uint8_t tlv_id_kp_serverAuth[] = {
--- a/services/healthreport/healthreporter.jsm +++ b/services/healthreport/healthreporter.jsm @@ -333,20 +333,22 @@ function AbstractHealthReporter(branch, this._lastDailyDate = null; // Yes, this will probably run concurrently with remaining constructor work. let hasFirstRun = this._prefs.get("service.firstRun", false); this._initHistogram = hasFirstRun ? TELEMETRY_INIT : TELEMETRY_INIT_FIRSTRUN; this._dbOpenHistogram = hasFirstRun ? TELEMETRY_DB_OPEN : TELEMETRY_DB_OPEN_FIRSTRUN; - // This is set to the name for the provider that we are currently initializing - // or shutting down, if any. This is used for AsyncShutdownTimeout diagnostics. + // This is set to the name for the provider that we are currently initializing, + // shutting down or collecting data from, if any. + // This is used for AsyncShutdownTimeout diagnostics. this._currentProviderInShutdown = null; this._currentProviderInInit = null; + this._currentProviderInCollect = null; } AbstractHealthReporter.prototype = Object.freeze({ QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), /** * Whether the service is fully initialized and running. * @@ -410,16 +412,17 @@ AbstractHealthReporter.prototype = Objec initializeHadError: this._initializeHadError, providerManagerInProgress: this._providerManagerInProgress, storageInProgress: this._storageInProgress, hasProviderManager: !!this._providerManager, hasStorage: !!this._storage, shutdownComplete: this._shutdownComplete, currentProviderInShutdown: this._currentProviderInShutdown, currentProviderInInit: this._currentProviderInInit, + currentProviderInCollect: this._currentProviderInCollect, })); try { this._storageInProgress = true; TelemetryStopwatch.start(this._dbOpenHistogram, this); let storage = yield Metrics.Storage(this._dbName); TelemetryStopwatch.finish(this._dbOpenHistogram, this); yield this._onStorageCreated(); @@ -788,17 +791,18 @@ AbstractHealthReporter.prototype = Objec return Promise.reject(new Error("Not initialized.")); } return Task.spawn(function doCollection() { yield this._providerManager.ensurePullOnlyProvidersRegistered(); try { TelemetryStopwatch.start(TELEMETRY_COLLECT_CONSTANT, this); - yield this._providerManager.collectConstantData(); + yield this._providerManager.collectConstantData(name => this._currentProviderInCollect = name); + this._currentProviderInCollect = null; TelemetryStopwatch.finish(TELEMETRY_COLLECT_CONSTANT, this); } catch (ex) { TelemetryStopwatch.cancel(TELEMETRY_COLLECT_CONSTANT, this); this._log.warn("Error collecting constant data: " + CommonUtils.exceptionStr(ex)); } // Daily data is collected if it hasn't yet been collected this @@ -808,17 +812,18 @@ AbstractHealthReporter.prototype = Objec // makes no guarantees about limits. The alternative would involve // recording state. The simpler implementation prevails for now. if (!this._lastDailyDate || Date.now() - this._lastDailyDate > MILLISECONDS_PER_DAY) { try { TelemetryStopwatch.start(TELEMETRY_COLLECT_DAILY, this); this._lastDailyDate = new Date(); - yield this._providerManager.collectDailyData(); + yield this._providerManager.collectDailyData(name => this._currentProviderInCollect = name); + this._currentProviderInCollect = null; TelemetryStopwatch.finish(TELEMETRY_COLLECT_DAILY, this); } catch (ex) { TelemetryStopwatch.cancel(TELEMETRY_COLLECT_DAILY, this); this._log.warn("Error collecting daily data from providers: " + CommonUtils.exceptionStr(ex)); } }
--- a/services/metrics/providermanager.jsm +++ b/services/metrics/providermanager.jsm @@ -431,18 +431,21 @@ this.ProviderManager.prototype = Object. /** * Collects all constant measurements from all providers. * * Returns a Promise that will be fulfilled once all data providers have * provided their constant data. A side-effect of this promise fulfillment * is that the manager is populated with the obtained collection results. * The resolved value to the promise is this `ProviderManager` instance. + * + * @param providerDiagnostic + * (function) Optional, called with the name of the provider currently being initialized. */ - collectConstantData: function () { + collectConstantData: function (providerDiagnostic=null) { let entries = []; for (let [name, entry] of this._providers) { if (entry.constantsCollected) { this._log.trace("Provider has already provided constant data: " + name); continue; } @@ -450,28 +453,30 @@ this.ProviderManager.prototype = Object. entries.push(entry); } let onCollect = function (entry, result) { entry.constantsCollected = true; }; return this._callCollectOnProviders(entries, "collectConstantData", - onCollect); + onCollect, providerDiagnostic); }, /** * Calls collectDailyData on all providers. */ - collectDailyData: function () { + collectDailyData: function (providerDiagnostic=null) { return this._callCollectOnProviders(this._providers.values(), - "collectDailyData"); + "collectDailyData", + null, + providerDiagnostic); }, - _callCollectOnProviders: function (entries, fnProperty, onCollect=null) { + _callCollectOnProviders: function (entries, fnProperty, onCollect=null, providerDiagnostic=null) { let promises = []; for (let entry of entries) { let provider = entry.provider; let collectPromise; try { collectPromise = provider[fnProperty].call(provider); } catch (ex) { @@ -497,31 +502,35 @@ this.ProviderManager.prototype = Object. } return CommonUtils.laterTickResolvingPromise(result); }); promises.push([provider.name, promise]); } - return this._handleCollectionPromises(promises); + return this._handleCollectionPromises(promises, providerDiagnostic); }, /** * Handles promises returned by the collect* functions. * * This consumes the data resolved by the promises and returns a new promise * that will be resolved once all promises have been resolved. * * The promise is resolved even if one of the underlying collection * promises is rejected. */ - _handleCollectionPromises: function (promises) { + _handleCollectionPromises: function (promises, providerDiagnostic=null) { return Task.spawn(function waitForPromises() { for (let [name, promise] of promises) { + if (providerDiagnostic) { + providerDiagnostic(name); + } + try { yield promise; this._log.debug("Provider collected successfully: " + name); } catch (ex) { this._recordProviderError(name, "Failed to collect", ex); } }
--- a/testing/config/mozharness/mac_config.py +++ b/testing/config/mozharness/mac_config.py @@ -4,83 +4,90 @@ config = { "suite_definitions": { "cppunittest": { "options": [ "--symbols-path=%(symbols_path)s", "--xre-path=%(abs_app_dir)s" ], + "mac_res_subdir": "MacOS", "run_filename": "runcppunittests.py", "testsdir": "cppunittests" }, "jittest": { "options": [ "tests/bin/js", "--no-slow", "--no-progress", "--tinderbox", "--tbpl" ], + "mac_res_subdir": "MacOS", "run_filename": "jit_test.py", "testsdir": "jit-test/jit-test" }, "mochitest": { "options": [ "--appname=%(binary_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", "--certificate-path=tests/certs", "--autorun", "--close-when-done", "--console-level=INFO", "--quiet", "--log-raw=%(raw_log_file)s" ], + "mac_res_subdir": "MacOS", "run_filename": "runtests.py", "testsdir": "mochitest" }, "mozbase": { "options": [ "-b", "%(binary_path)s" ], + "mac_res_subdir": "MacOS", "run_filename": "test.py", "testsdir": "mozbase" }, "reftest": { "options": [ "--appname=%(binary_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s" ], + "mac_res_subdir": "MacOS", "run_filename": "runreftest.py", "testsdir": "reftest" }, "webapprt": { "options": [ "--app=%(app_path)s", "--utility-path=tests/bin", "--extra-profile-file=tests/bin/plugins", "--symbols-path=%(symbols_path)s", "--certificate-path=tests/certs", "--autorun", "--close-when-done", "--console-level=INFO", "--testing-modules-dir=tests/modules", "--quiet" ], + "mac_res_subdir": "MacOS", "run_filename": "runtests.py", "testsdir": "mochitest" }, "xpcshell": { "options": [ "--symbols-path=%(symbols_path)s", "--test-plugin-path=%(test_plugin_path)s", "--log-raw=%(raw_log_file)s" ], + "mac_res_subdir": "MacOS", "run_filename": "runxpcshelltests.py", "testsdir": "xpcshell" } } -} \ No newline at end of file +}
--- a/testing/mochitest/b2g_start_script.js +++ b/testing/mochitest/b2g_start_script.js @@ -1,16 +1,17 @@ /* 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/. */ let outOfProcess = __marionetteParams[0] let mochitestUrl = __marionetteParams[1] let onDevice = __marionetteParams[2] let wifiSettings = __marionetteParams[3] +let chrome = __marionetteParams[4] let prefs = Components.classes["@mozilla.org/preferences-service;1"]. getService(Components.interfaces.nsIPrefBranch) let settings = window.navigator.mozSettings; let cm = Components.classes["@mozilla.org/categorymanager;1"]. getService(Components.interfaces.nsICategoryManager); if (wifiSettings) wifiSettings = JSON.parse(wifiSettings); @@ -81,16 +82,21 @@ if (outOfProcess) { removeEventListener("DOMWindowCreated", listener, false); var window = e.target.defaultView; window.wrappedJSObject.SpecialPowers.addPermission("allowXULXBL", true, window.document); }); } mm.loadFrameScript("data:,(" + encodeURI(contentScript.toSource()) + ")();", true); } +if (chrome) { + let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader); + loader.loadSubScript("chrome://mochikit/content/browser-test.js"); + b2gStart(); +} if (onDevice) { var cpuLock = Cc["@mozilla.org/power/powermanagerservice;1"] .getService(Ci.nsIPowerManagerService) .newWakeLock("cpu"); let manager = navigator.mozWifiManager; let con = manager.connection; @@ -128,10 +134,12 @@ if (onDevice) { req.onsuccess = function () { dump("----------------------enabling wifi------------------\n"); var req1 = settings.createLock().set({ 'wifi.enabled': true, 'wifi.suspended': false}); }; } } else { - container.src = mochitestUrl; + if (!chrome) { + container.src = mochitestUrl; + } }
--- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -1,16 +1,20 @@ /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */ // Test timeout (seconds) var gTimeoutSeconds = 45; var gConfig; if (Cc === undefined) { var Cc = Components.classes; +} +if (Ci === undefined) { var Ci = Components.interfaces; +} +if (Cu === undefined) { var Cu = Components.utils; } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Task.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm"); @@ -27,16 +31,25 @@ const SIMPLETEST_OVERRIDES = window.addEventListener("load", function testOnLoad() { window.removeEventListener("load", testOnLoad); window.addEventListener("MozAfterPaint", function testOnMozAfterPaint() { window.removeEventListener("MozAfterPaint", testOnMozAfterPaint); setTimeout(testInit, 0); }); }); +function b2gStart() { + let homescreen = document.getElementById('systemapp'); + var webNav = homescreen.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation); + var url = "chrome://mochikit/content/harness.xul?manifestFile=tests.json"; + + webNav.loadURI(url, null, null, null, null); +} + function testInit() { gConfig = readConfig(); if (gConfig.testRoot == "browser" || gConfig.testRoot == "metro" || gConfig.testRoot == "webapprtChrome") { // Make sure to launch the test harness for the first opened window only var prefs = Services.prefs; if (prefs.prefHasUserValue("testing.browserTestHarness.running"))
--- a/testing/mochitest/mach_commands.py +++ b/testing/mochitest/mach_commands.py @@ -106,17 +106,17 @@ class MochitestRunner(MozbuildObject): sys.path.append(build_path) self.tests_dir = os.path.join(self.topobjdir, '_tests') self.mochitest_dir = os.path.join(self.tests_dir, 'testing', 'mochitest') self.bin_dir = os.path.join(self.topobjdir, 'dist', 'bin') def run_b2g_test(self, test_paths=None, b2g_home=None, xre_path=None, total_chunks=None, this_chunk=None, no_window=None, - repeat=0, run_until_failure=False, **kwargs): + repeat=0, run_until_failure=False, chrome=False, **kwargs): """Runs a b2g mochitest. test_paths is an enumerable of paths to tests. It can be a relative path from the top source directory, an absolute filename, or a directory containing test files. """ # Need to call relpath before os.chdir() below. test_path = '' @@ -141,17 +141,20 @@ class MochitestRunner(MozbuildObject): import mochitest from mochitest_options import B2GOptions parser = B2GOptions() options = parser.parse_args([])[0] if test_path: - test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path) + if chrome: + test_root_file = mozpack.path.join(self.mochitest_dir, 'chrome', test_path) + else: + test_root_file = mozpack.path.join(self.mochitest_dir, 'tests', test_path) if not os.path.exists(test_root_file): print('Specified test path does not exist: %s' % test_root_file) return 1 options.testPath = test_path for k, v in kwargs.iteritems(): setattr(options, k, v) options.noWindow = no_window @@ -179,16 +182,17 @@ class MochitestRunner(MozbuildObject): # TODO Find adb automatically if it isn't on the path print(ADB_NOT_FOUND % ('mochitest-remote', b2g_home)) return 1 options.b2gPath = b2g_home options.logdir = self.mochitest_dir options.httpdPath = self.mochitest_dir options.xrePath = xre_path + options.chrome = chrome return mochitest.run_remote_mochitests(parser, options) def run_desktop_test(self, context, suite=None, test_paths=None, debugger=None, debugger_args=None, slowscript=False, screenshot_on_fail = False, shuffle=False, closure_behaviour='auto', rerun_failures=False, no_autorun=False, repeat=0, run_until_failure=False, slow=False, chunk_by_dir=0, total_chunks=None, this_chunk=None, extraPrefs=[], jsdebugger=False, debug_on_failure=False, start_at=None, end_at=None, e10s=False, strict_content_sandbox=False, dmd=False, dump_output_directory=None, @@ -836,16 +840,23 @@ class B2GCommands(MachCommandBase): driver = self._spawn(BuildDriver) driver.install_tests(remove=False) mochitest = self._spawn(MochitestRunner) return mochitest.run_b2g_test(b2g_home=self.b2g_home, xre_path=self.xre_path, test_paths=test_paths, **kwargs) + @Command('mochitest-chrome-remote', category='testing', + description='Run a remote mochitest-chrome.', + conditions=[conditions.is_b2g, is_emulator]) + @B2GCommand + def run_mochitest_chrome_remote(self, test_paths, **kwargs): + return self.run_mochitest_remote(test_paths, chrome=True, **kwargs) + @Command('mochitest-b2g-desktop', category='testing', conditions=[conditions.is_b2g_desktop], description='Run a b2g desktop mochitest (same as mochitest-plain but for b2g desktop).') @B2GCommand def run_mochitest_b2g_desktop(self, test_paths, **kwargs): kwargs['profile'] = kwargs.get('profile') or os.environ.get('GAIA_PROFILE') if not kwargs['profile'] or not os.path.isdir(kwargs['profile']): print(GAIA_PROFILE_NOT_FOUND % 'mochitest-b2g-desktop')
--- a/testing/mochitest/runtests.py +++ b/testing/mochitest/runtests.py @@ -781,16 +781,28 @@ class MochitestUtilsMixin(object): mfile.write(chrome) def getChromeTestDir(self, options): dir = os.path.join(os.path.abspath("."), SCRIPT_DIR) + "/" if mozinfo.isWin: dir = "file:///" + dir.replace("\\", "/") return dir + def writeChromeManifest(self, options): + manifest = os.path.join(options.profilePath, "tests.manifest") + with open(manifest, "w") as manifestFile: + # Register chrome directory. + chrometestDir = self.getChromeTestDir(options) + manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir) + + if options.testingModulesDir is not None: + manifestFile.write("resource testing-common file:///%s\n" % + options.testingModulesDir) + return manifest + def addChromeToProfile(self, options): "Adds MochiKit chrome tests to the profile." # Create (empty) chrome directory. chromedir = os.path.join(options.profilePath, "chrome") os.mkdir(chromedir) # Write userChrome.css. @@ -802,25 +814,17 @@ toolbarpalette { } toolbar#nav-bar { background-image: none !important; } """ with open(os.path.join(options.profilePath, "userChrome.css"), "a") as chromeFile: chromeFile.write(chrome) - manifest = os.path.join(options.profilePath, "tests.manifest") - with open(manifest, "w") as manifestFile: - # Register chrome directory. - chrometestDir = self.getChromeTestDir(options) - manifestFile.write("content mochitests %s contentaccessible=yes\n" % chrometestDir) - - if options.testingModulesDir is not None: - manifestFile.write("resource testing-common file:///%s\n" % - options.testingModulesDir) + manifest = self.writeChromeManifest(options) # Call installChromeJar(). if not os.path.isdir(os.path.join(SCRIPT_DIR, self.jarDir)): self.log.error("TEST-UNEXPECTED-FAIL | invalid setup: missing mochikit extension") return None # Support Firefox (browser), B2G (shell), SeaMonkey (navigator), and Webapp # Runtime (webapp).
--- a/testing/mochitest/runtestsb2g.py +++ b/testing/mochitest/runtestsb2g.py @@ -35,16 +35,17 @@ class B2GMochitest(MochitestUtilsMixin): self.marionette_args = marionette_args self.out_of_process = out_of_process self.locations_file = locations self.preferences = [] self.webapps = None self.test_script = os.path.join(here, 'b2g_start_script.js') self.test_script_args = [self.out_of_process] self.product = 'b2g' + self.remote_chrome_test_dir = None if profile_data_dir: self.preferences = [os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('pref')] self.webapps = [os.path.join(profile_data_dir, f) for f in os.listdir(profile_data_dir) if f.startswith('webapp')] # mozinfo is populated by the parent class @@ -147,16 +148,20 @@ class B2GMochitest(MochitestUtilsMixin): self.runner = self.marionette.runner self.app_ctx = self.runner.app_ctx self.remote_log = posixpath.join(self.app_ctx.remote_test_root, 'log', 'mochitest.log') if not self.app_ctx.dm.dirExists(posixpath.dirname(self.remote_log)): self.app_ctx.dm.mkDirs(self.remote_log) + if options.chrome: + # Update chrome manifest file in profile with correct path. + self.writeChromeManifest(options) + self.leak_report_file = posixpath.join(self.app_ctx.remote_test_root, 'log', 'runtests_leaks.log') # We don't want to copy the host env onto the device, so pass in an # empty env. self.browserEnv = self.buildBrowserEnv(options, env={}) # B2G emulator debug tests still make external connections, so don't @@ -164,16 +169,17 @@ class B2GMochitest(MochitestUtilsMixin): if mozinfo.info['debug'] and 'MOZ_DISABLE_NONLOCAL_CONNECTIONS' in self.browserEnv: del self.browserEnv['MOZ_DISABLE_NONLOCAL_CONNECTIONS'] self.runner.env.update(self.browserEnv) self.startServers(options, None) self.buildURLOptions(options, {'MOZ_HIDE_RESULTS_TABLE': '1'}) self.test_script_args.append(not options.emulator) self.test_script_args.append(options.wifi) + self.test_script_args.append(options.chrome) self.runner.start(outputTimeout=timeout) self.marionette.wait_for_port() self.marionette.start_session() self.marionette.set_context(self.marionette.CONTEXT_CHROME) @@ -181,16 +187,25 @@ class B2GMochitest(MochitestUtilsMixin): # will be 'offline' when the mochitests start. Presumably, the network # won't be offline on a real device, so we only do this for emulators. self.marionette.execute_script(""" Components.utils.import("resource://gre/modules/Services.jsm"); Services.io.manageOfflineStatus = false; Services.io.offline = false; """) + if options.chrome: + self.app_ctx.dm.removeDir(self.remote_chrome_test_dir) + self.app_ctx.dm.mkDir(self.remote_chrome_test_dir) + local = super(B2GMochitest, self).getChromeTestDir(options) + local = os.path.join(local, "chrome") + remote = self.remote_chrome_test_dir + self.log.info("pushing %s to %s on device..." % (local, remote)) + self.app_ctx.dm.pushDir(local, remote) + if os.path.isfile(self.test_script): with open(self.test_script, 'r') as script: self.marionette.execute_script(script.read(), script_args=self.test_script_args) else: self.marionette.execute_script(self.test_script, script_args=self.test_script_args) status = self.runner.wait() @@ -221,16 +236,28 @@ class B2GMochitest(MochitestUtilsMixin): if manifest is not None: self.cleanup(manifest, options) return status def getGMPPluginPath(self, options): # TODO: bug 1043403 return None + def getChromeTestDir(self, options): + # The chrome test directory returned here is the remote location + # of chrome test files. A reference to this directory is requested + # when building the profile locally, before self.app_ctx is defined. + # To get around this, return a dummy directory until self.app_ctx + # is defined; the correct directory will be returned later, over- + # writing the dummy. + if hasattr(self, 'app_ctx'): + self.remote_chrome_test_dir = posixpath.join(self.app_ctx.remote_test_root, 'chrome'); + return self.remote_chrome_test_dir + return 'dummy-chrome-test-dir' + class B2GDeviceMochitest(B2GMochitest, Mochitest): remote_log = None def __init__(self, marionette_args, logger_options, profile_data_dir, local_binary_dir, remote_test_root=None, remote_log_file=None): B2GMochitest.__init__(self, marionette_args, logger_options, out_of_process=True, profile_data_dir=profile_data_dir) self.local_log = None
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -815,17 +815,17 @@ SimpleTest.waitForClipboard = function(a // Don't show the success message when waiting for preExpectedVal if (preExpectedVal) preExpectedVal = null; else SimpleTest.ok(true, "Clipboard has the correct value"); reset(); successFn(); } else { - setTimeout(function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100); + SimpleTest._originalSetTimeout.apply(window, [function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100]); } } // First we wait for a known value different from the expected one. var preExpectedVal = SimpleTest._waitForClipboardMonotonicCounter + "-waitForClipboard-known-value"; SpecialPowers.clipboardCopyString(preExpectedVal); wait(function(aData) { return aData == preExpectedVal; },
--- a/toolkit/modules/RemoteWebProgress.jsm +++ b/toolkit/modules/RemoteWebProgress.jsm @@ -99,21 +99,16 @@ RemoteWebProgressManager.prototype = { if (aStatus) { let helper = Cc["@mozilla.org/network/serialization-helper;1"] .getService(Components.interfaces.nsISerializationHelper); deserialized = helper.deserializeObject(aStatus) deserialized.QueryInterface(Ci.nsISSLStatus); } - // We must check the Extended Validation (EV) state here, on the chrome - // process, because NSS is needed for that determination. - if (deserialized && deserialized.isExtendedValidation) - aState |= Ci.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL; - return [deserialized, aState]; }, setCurrentURI: function (aURI) { // This function is simpler than nsDocShell::SetCurrentURI since // it doesn't have to deal with child docshells. let webNavigation = this._browser.webNavigation; webNavigation._currentURI = aURI;
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp +++ b/toolkit/xre/test/win/TestDllInterceptor.cpp @@ -152,16 +152,17 @@ int main() TestHook("ntdll.dll", "NtQueryFullAttributesFile") && // Bug 733892: toolkit/crashreporter/nsExceptionHandler.cpp TestHook("kernel32.dll", "SetUnhandledExceptionFilter") && #ifdef _M_IX86 // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp TestHook("kernel32.dll", "VirtualAlloc") && TestHook("kernel32.dll", "MapViewOfFile") && TestHook("gdi32.dll", "CreateDIBSection") && + TestHook("kernel32.dll", "CreateFileW") && #endif TestDetour("ntdll.dll", "LdrLoadDll")) { printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n"); return 0; } return 1; }
--- a/xpcom/base/AlreadyAddRefed.h +++ b/xpcom/base/AlreadyAddRefed.h @@ -132,12 +132,12 @@ struct already_AddRefed already_AddRefed<U> downcast() { U* tmp = static_cast<U*>(mRawPtr); mRawPtr = nullptr; return already_AddRefed<U>(tmp); } private: - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; }; #endif // AlreadyAddRefed_h
--- a/xpcom/base/StaticPtr.h +++ b/xpcom/base/StaticPtr.h @@ -136,17 +136,17 @@ private: { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { oldPtr->Release(); } } - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; }; namespace StaticPtr_internal { class Zero; } // namespace StaticPtr_internal #define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \ template<__VA_ARGS__> \
--- a/xpcom/base/nsAutoPtr.h +++ b/xpcom/base/nsAutoPtr.h @@ -55,21 +55,21 @@ private: } operator T*() const { return mPtr; } private: - T* mPtr; + T* MOZ_NON_OWNING_REF mPtr; }; private: - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; public: typedef T element_type; ~nsAutoPtr() { delete mRawPtr; } @@ -426,17 +426,17 @@ private: assign(T* aNewPtr) { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; delete [] oldPtr; } private: - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; public: typedef T element_type; ~nsAutoArrayPtr() { delete [] mRawPtr; } @@ -773,17 +773,17 @@ public: virtual nsresult NS_FASTCALL operator()(const nsIID& aIID, void** aResult) const { nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult) : NS_ERROR_NULL_POINTER; return status; } private: - T* mRawPtr; + T* MOZ_NON_OWNING_REF mRawPtr; }; template<class T> class nsQueryObjectWithError : public nsCOMPtr_helper { public: nsQueryObjectWithError(T* aRawPtr, nsresult* aErrorPtr) : mRawPtr(aRawPtr), mErrorPtr(aErrorPtr) @@ -796,17 +796,17 @@ public: nsresult status = mRawPtr ? mRawPtr->QueryInterface(aIID, aResult) : NS_ERROR_NULL_POINTER; if (mErrorPtr) { *mErrorPtr = status; } return status; } private: - T* mRawPtr; + T* MOZ_NON_OWNING_REF mRawPtr; nsresult* mErrorPtr; }; template<class T> inline nsQueryObject<T> do_QueryObject(T* aRawPtr) { return nsQueryObject<T>(aRawPtr);
--- a/xpcom/base/nsRefPtr.h +++ b/xpcom/base/nsRefPtr.h @@ -43,17 +43,17 @@ private: T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { oldPtr->Release(); } } private: - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; public: typedef T element_type; ~nsRefPtr() { if (mRawPtr) { mRawPtr->Release();
--- a/xpcom/build/nsWindowsDllInterceptor.h +++ b/xpcom/build/nsWindowsDllInterceptor.h @@ -392,16 +392,19 @@ protected: nBytes++; } else if (origBytes[nBytes] == 0x6A) { // PUSH imm8 nBytes += 2; } else if (origBytes[nBytes] == 0xe9) { pJmp32 = nBytes; // jmp 32bit offset nBytes += 5; + } else if (origBytes[nBytes] == 0xff && origBytes[nBytes + 1] == 0x25) { + // jmp [disp32] + nBytes += 6; } else { //printf ("Unknown x86 instruction byte 0x%02x, aborting trampoline\n", origBytes[nBytes]); return; } } #elif defined(_M_X64) byteptr_t directJmpAddr;
--- a/xpcom/build/nsXREAppData.h +++ b/xpcom/build/nsXREAppData.h @@ -2,16 +2,17 @@ /* 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 nsXREAppData_h #define nsXREAppData_h #include <stdint.h> +#include "mozilla/Attributes.h" class nsIFile; /** * Application-specific data needed to start the apprunner. * * @note When this structure is allocated and manipulated by XRE_CreateAppData, * string fields will be allocated with NS_Alloc, and interface pointers @@ -25,17 +26,17 @@ struct nsXREAppData * is maintained. */ uint32_t size; /** * The directory of the application to be run. May be null if the * xulrunner and the app are installed into the same directory. */ - nsIFile* directory; + nsIFile* MOZ_NON_OWNING_REF directory; /** * The name of the application vendor. This must be ASCII, and is normally * mixed-case, e.g. "Mozilla". Optional (may be null), but highly * recommended. Must not be the empty string. */ const char* vendor; @@ -87,17 +88,17 @@ struct nsXREAppData * Combination of NS_XRE_ prefixed flags (defined below). */ uint32_t flags; /** * The location of the XRE. XRE_main may not be able to figure this out * programatically. */ - nsIFile* xreDirectory; + nsIFile* MOZ_NON_OWNING_REF xreDirectory; /** * The minimum/maximum compatible XRE version. */ const char* minVersion; const char* maxVersion; /**
--- a/xpcom/glue/nsCOMPtr.h +++ b/xpcom/glue/nsCOMPtr.h @@ -162,32 +162,32 @@ class MOZ_STACK_CLASS nsQueryInterface M { public: explicit nsQueryInterface(nsISupports* aRawPtr) : mRawPtr(aRawPtr) {} nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const; private: - nsISupports* mRawPtr; + nsISupports* MOZ_OWNING_REF mRawPtr; }; class nsQueryInterfaceWithError { public: nsQueryInterfaceWithError(nsISupports* aRawPtr, nsresult* aError) : mRawPtr(aRawPtr) , mErrorPtr(aError) { } nsresult NS_FASTCALL operator()(const nsIID& aIID, void**) const; private: - nsISupports* mRawPtr; + nsISupports* MOZ_OWNING_REF mRawPtr; nsresult* mErrorPtr; }; inline nsQueryInterface do_QueryInterface(nsISupports* aRawPtr) { return nsQueryInterface(aRawPtr); } @@ -314,17 +314,17 @@ public: assign_from_gs_contractid_with_error(const nsGetServiceByContractIDWithError&, const nsIID&); void NS_FASTCALL assign_from_helper(const nsCOMPtr_helper&, const nsIID&); void** NS_FASTCALL begin_assignment(); protected: - NS_MAY_ALIAS_PTR(nsISupports) mRawPtr; + NS_MAY_ALIAS_PTR(nsISupports) MOZ_OWNING_REF mRawPtr; void assign_assuming_AddRef(nsISupports* aNewPtr) { // |AddRef()|ing the new value (before entering this function) before // |Release()|ing the old lets us safely ignore the self-assignment case. // We must, however, be careful only to |Release()| _after_ doing the // assignment, in case the |Release()| leads to our _own_ destruction, // which would, in turn, cause an incorrect second |Release()| of our old @@ -373,17 +373,17 @@ private: NSCAP_LOG_ASSIGNMENT(this, aNewPtr); NSCAP_LOG_RELEASE(this, oldPtr); if (oldPtr) { NSCAP_RELEASE(this, oldPtr); } } private: - T* mRawPtr; + T* MOZ_OWNING_REF mRawPtr; #endif public: typedef T element_type; #ifndef NSCAP_FEATURE_USE_BASE ~nsCOMPtr() {
--- a/xpcom/glue/nsComponentManagerUtils.h +++ b/xpcom/glue/nsComponentManagerUtils.h @@ -36,17 +36,17 @@ public: , mErrorPtr(aErrorPtr) { } virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const nsCID& mCID; - nsISupports* mOuter; + nsISupports* MOZ_NON_OWNING_REF mOuter; nsresult* mErrorPtr; }; class nsCreateInstanceByContractID : public nsCOMPtr_helper { public: nsCreateInstanceByContractID(const char* aContractID, nsISupports* aOuter, nsresult* aErrorPtr) @@ -55,17 +55,17 @@ public: , mErrorPtr(aErrorPtr) { } virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: const char* mContractID; - nsISupports* mOuter; + nsISupports* MOZ_NON_OWNING_REF mOuter; nsresult* mErrorPtr; }; class nsCreateInstanceFromFactory : public nsCOMPtr_helper { public: nsCreateInstanceFromFactory(nsIFactory* aFactory, nsISupports* aOuter, nsresult* aErrorPtr) @@ -73,18 +73,18 @@ public: , mOuter(aOuter) , mErrorPtr(aErrorPtr) { } virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: - nsIFactory* mFactory; - nsISupports* mOuter; + nsIFactory* MOZ_NON_OWNING_REF mFactory; + nsISupports* MOZ_NON_OWNING_REF mOuter; nsresult* mErrorPtr; }; inline const nsCreateInstanceByCID do_CreateInstance(const nsCID& aCID, nsresult* aError = 0) { return nsCreateInstanceByCID(aCID, 0, aError);
--- a/xpcom/glue/nsEnumeratorUtils.cpp +++ b/xpcom/glue/nsEnumeratorUtils.cpp @@ -106,30 +106,28 @@ public: NS_IMETHOD GetNext(nsISupports** aResult); explicit nsSingletonEnumerator(nsISupports* aValue); private: ~nsSingletonEnumerator(); protected: - nsISupports* mValue; + nsCOMPtr<nsISupports> mValue; bool mConsumed; }; nsSingletonEnumerator::nsSingletonEnumerator(nsISupports* aValue) : mValue(aValue) { - NS_IF_ADDREF(mValue); mConsumed = (mValue ? false : true); } nsSingletonEnumerator::~nsSingletonEnumerator() { - NS_IF_RELEASE(mValue); } NS_IMPL_ISUPPORTS(nsSingletonEnumerator, nsISimpleEnumerator) NS_IMETHODIMP nsSingletonEnumerator::HasMoreElements(bool* aResult) { NS_PRECONDITION(aResult != 0, "null ptr");
--- a/xpcom/glue/nsHashKeys.h +++ b/xpcom/glue/nsHashKeys.h @@ -380,17 +380,17 @@ public: static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; } static PLDHashNumber HashKey(KeyTypePointer aKey) { return NS_PTR_TO_UINT32(aKey) >> 2; } enum { ALLOW_MEMMOVE = true }; protected: - T* mKey; + T* MOZ_NON_OWNING_REF mKey; }; /** * hashkey wrapper using T* KeyType that sets key to nullptr upon * destruction. Relevant only in cases where a memory pointer-scanner * like valgrind might get confused about stale references. * * @see nsTHashtable::EntryType for specification
--- a/xpcom/glue/nsIInterfaceRequestorUtils.h +++ b/xpcom/glue/nsIInterfaceRequestorUtils.h @@ -29,17 +29,17 @@ public: : mSource(aSource) , mErrorPtr(aError) { } virtual nsresult NS_FASTCALL operator()(const nsIID&, void**) const; private: - nsISupports* mSource; + nsISupports* MOZ_NON_OWNING_REF mSource; nsresult* mErrorPtr; }; inline const nsGetInterface do_GetInterface(nsISupports* aSource, nsresult* aError = 0) { return nsGetInterface(aSource, aError); }
--- a/xpcom/glue/nsProxyRelease.cpp +++ b/xpcom/glue/nsProxyRelease.cpp @@ -15,17 +15,17 @@ public: NS_IMETHOD Run() { mDoomed->Release(); return NS_OK; } private: - nsISupports* mDoomed; + nsISupports* MOZ_OWNING_REF mDoomed; }; nsresult NS_ProxyRelease(nsIEventTarget* aTarget, nsISupports* aDoomed, bool aAlwaysProxy) { nsresult rv;
--- a/xpcom/glue/nsThreadUtils.h +++ b/xpcom/glue/nsThreadUtils.h @@ -266,45 +266,43 @@ public: // Make sure this return type is safe. typedef typename ReturnTypeEnforcer<ReturnType>::ReturnTypeIsSafe check; }; template<class ClassType, typename Arg, bool Owning> struct nsRunnableMethodReceiver { - ClassType* mObj; + nsRefPtr<ClassType> mObj; Arg mArg; nsRunnableMethodReceiver(ClassType* aObj, Arg aArg) : mObj(aObj) , mArg(aArg) { - NS_IF_ADDREF(mObj); } ~nsRunnableMethodReceiver() { Revoke(); } - void Revoke() { NS_IF_RELEASE(mObj); } + void Revoke() { mObj = nullptr; } }; template<class ClassType, bool Owning> struct nsRunnableMethodReceiver<ClassType, void, Owning> { - ClassType* mObj; + nsRefPtr<ClassType> mObj; explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) { - NS_IF_ADDREF(mObj); } ~nsRunnableMethodReceiver() { Revoke(); } - void Revoke() { NS_IF_RELEASE(mObj); } + void Revoke() { mObj = nullptr; } }; template<class ClassType> struct nsRunnableMethodReceiver<ClassType, void, false> { - ClassType* mObj; + ClassType* MOZ_NON_OWNING_REF mObj; explicit nsRunnableMethodReceiver(ClassType* aObj) : mObj(aObj) {} void Revoke() { mObj = nullptr; } }; template<typename Method, bool Owning> struct nsRunnableMethodTraits; template<class C, typename R, typename A, bool Owning> struct nsRunnableMethodTraits<R(C::*)(A), Owning>
--- a/xpcom/glue/nsWeakReference.cpp +++ b/xpcom/glue/nsWeakReference.cpp @@ -40,17 +40,17 @@ private: void NoticeReferentDestruction() // ...called (only) by an |nsSupportsWeakReference| from _its_ dtor. { mReferent = 0; } - nsSupportsWeakReference* mReferent; + nsSupportsWeakReference* MOZ_NON_OWNING_REF mReferent; }; nsresult nsQueryReferent::operator()(const nsIID& aIID, void** aAnswer) const { nsresult status; if (mWeakPtr) { if (NS_FAILED(status = mWeakPtr->QueryReferent(aIID, aAnswer))) {
--- a/xpcom/glue/nsWeakReference.h +++ b/xpcom/glue/nsWeakReference.h @@ -26,17 +26,17 @@ protected: inline ~nsSupportsWeakReference(); private: friend class nsWeakReference; // Called (only) by an |nsWeakReference| from _its_ dtor. void NoticeProxyDestruction() { mProxy = 0; } - nsWeakReference* mProxy; + nsWeakReference* MOZ_NON_OWNING_REF mProxy; protected: void ClearWeakReferences(); bool HasWeakReferences() const { return mProxy != 0; } }; inline