mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Merge m-i to m-c, a=merge
This commit is contained in:
commit
dc8c1c5904
@ -8,7 +8,7 @@ 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)
|
||||
|
@ -642,8 +642,13 @@ 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
|
||||
|
@ -1343,7 +1343,6 @@ BrowserElementChild.prototype = {
|
||||
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 });
|
||||
|
@ -10,6 +10,7 @@
|
||||
#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"
|
||||
@ -43,9 +44,28 @@ AppleVDADecoder::AppleVDADecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
{
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
@ -270,17 +290,9 @@ AppleVDADecoder::OutputFrame(CVPixelBufferRef aImage,
|
||||
// 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()));
|
||||
@ -411,11 +423,11 @@ AppleVDADecoder::CreateDecoderSpecification()
|
||||
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,
|
||||
|
@ -90,6 +90,9 @@ public:
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
ReorderQueue mReorderQueue;
|
||||
uint32_t mPictureWidth;
|
||||
uint32_t mPictureHeight;
|
||||
uint32_t mMaxRefFrames;
|
||||
|
||||
private:
|
||||
VDADecoder mDecoder;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#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"
|
||||
@ -275,8 +276,8 @@ AppleVTDecoder::InitializeSession()
|
||||
|
||||
rv = CMVideoFormatDescriptionCreate(kCFAllocatorDefault,
|
||||
kCMVideoCodecType_H264,
|
||||
mConfig.image_width,
|
||||
mConfig.image_height,
|
||||
mPictureWidth,
|
||||
mPictureHeight,
|
||||
extensions,
|
||||
&mFormat);
|
||||
if (rv != noErr) {
|
||||
|
@ -265,7 +265,7 @@ private:
|
||||
void AssertInvariants() const;
|
||||
#endif
|
||||
|
||||
SharedMemory* mSegment;
|
||||
SharedMemory* MOZ_NON_OWNING_REF mSegment;
|
||||
void* mData;
|
||||
size_t mSize;
|
||||
id_t mId;
|
||||
|
@ -13,8 +13,6 @@ 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']
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "builtin/SIMD.h"
|
||||
|
||||
#include "mozilla/IntegerTypeTraits.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
@ -147,12 +148,17 @@ static bool SignMask(JSContext *cx, unsigned argc, Value *vp)
|
||||
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;
|
||||
}
|
||||
@ -296,7 +302,6 @@ 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) {
|
||||
@ -310,12 +315,6 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
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;
|
||||
@ -324,7 +323,7 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
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;
|
||||
@ -332,7 +331,7 @@ SimdTypeDescr::call(JSContext *cx, unsigned argc, Value *vp)
|
||||
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;
|
||||
|
@ -1,3 +1,8 @@
|
||||
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;
|
||||
|
@ -188,7 +188,7 @@ assertEq(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f() { var x=i4(0,-
|
||||
|
||||
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");
|
||||
|
@ -1791,6 +1791,11 @@ CodeGenerator::visitNop(LNop *lir)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitMop(LMop *lir)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
CodeGenerator::visitOsiPoint(LOsiPoint *lir)
|
||||
{
|
||||
|
@ -63,6 +63,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
||||
|
||||
void visitLabel(LLabel *lir);
|
||||
void visitNop(LNop *lir);
|
||||
void visitMop(LMop *lir);
|
||||
void visitOsiPoint(LOsiPoint *lir);
|
||||
void visitGoto(LGoto *lir);
|
||||
void visitTableSwitch(LTableSwitch *ins);
|
||||
|
@ -21,10 +21,10 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *lsh)
|
||||
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;
|
||||
|
||||
@ -47,8 +47,8 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *lsh)
|
||||
|
||||
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;
|
||||
@ -72,11 +72,11 @@ AnalyzeLsh(TempAllocator &alloc, MLsh *lsh)
|
||||
|
||||
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;
|
||||
|
||||
|
@ -260,7 +260,7 @@ MaybeFoldConditionBlock(MIRGraph &graph, MBasicBlock *initialBlock)
|
||||
|
||||
MBasicBlock *trueTarget = trueBranch;
|
||||
if (BlockComputesConstant(trueBranch, trueResult)) {
|
||||
trueTarget = trueResult->toConstant()->valueToBoolean()
|
||||
trueTarget = trueResult->constantToBoolean()
|
||||
? finalTest->ifTrue()
|
||||
: finalTest->ifFalse();
|
||||
testBlock->removePredecessor(trueBranch);
|
||||
@ -272,7 +272,7 @@ MaybeFoldConditionBlock(MIRGraph &graph, MBasicBlock *initialBlock)
|
||||
|
||||
MBasicBlock *falseTarget = falseBranch;
|
||||
if (BlockComputesConstant(falseBranch, falseResult)) {
|
||||
falseTarget = falseResult->toConstant()->valueToBoolean()
|
||||
falseTarget = falseResult->constantToBoolean()
|
||||
? finalTest->ifTrue()
|
||||
: finalTest->ifFalse();
|
||||
testBlock->removePredecessor(falseBranch);
|
||||
@ -2270,8 +2270,8 @@ jit::ExtractLinearSum(MDefinition *ins)
|
||||
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()) {
|
||||
@ -2672,8 +2672,8 @@ LinearSum::add(MDefinition *term, int32_t scale)
|
||||
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);
|
||||
@ -2751,7 +2751,7 @@ jit::ConvertLinearSum(TempAllocator &alloc, MBasicBlock *block, const LinearSum
|
||||
|
||||
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);
|
||||
|
@ -2261,9 +2261,8 @@ IonBuilder::processDoWhileCondEnd(CFGState &state)
|
||||
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;
|
||||
|
||||
@ -4568,7 +4567,7 @@ IonBuilder::makeInliningDecision(JSObject *targetArg, CallInfo &callInfo)
|
||||
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)
|
||||
@ -5930,10 +5929,10 @@ IonBuilder::jsop_eval(uint32_t argc)
|
||||
// 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);
|
||||
@ -7846,10 +7845,10 @@ IonBuilder::getElemTryArgumentsInlined(bool *emitted, MDefinition *obj, MDefinit
|
||||
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)
|
||||
@ -8065,8 +8064,8 @@ IonBuilder::addTypedArrayLengthAndData(MDefinition *obj,
|
||||
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();
|
||||
|
||||
@ -8123,8 +8122,8 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *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;
|
||||
@ -8132,9 +8131,9 @@ IonBuilder::convertShiftToMaskForStaticTypedArray(MDefinition *id,
|
||||
|
||||
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;
|
||||
|
||||
|
@ -43,6 +43,12 @@ class LNop : public LInstructionHelper<0, 0, 0>
|
||||
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.
|
||||
//
|
||||
|
@ -10,6 +10,7 @@
|
||||
#define LIR_COMMON_OPCODE_LIST(_) \
|
||||
_(Label) \
|
||||
_(Nop) \
|
||||
_(Mop) \
|
||||
_(OsiPoint) \
|
||||
_(MoveGroup) \
|
||||
_(Integer) \
|
||||
|
@ -623,7 +623,7 @@ 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);
|
||||
@ -643,8 +643,8 @@ LIRGenerator::visitTest(MTest *test)
|
||||
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;
|
||||
}
|
||||
@ -1491,7 +1491,7 @@ LIRGenerator::visitMul(MMul *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);
|
||||
@ -1500,7 +1500,7 @@ LIRGenerator::visitMul(MMul *ins)
|
||||
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);
|
||||
@ -1509,7 +1509,7 @@ LIRGenerator::visitMul(MMul *ins)
|
||||
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);
|
||||
@ -4117,10 +4117,20 @@ LIRGenerator::visitInstruction(MInstruction *ins)
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -376,7 +376,7 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
return InliningStatus_NotInlined;
|
||||
|
||||
MDefinition *arg = callInfo.getArg(0);
|
||||
if (!arg->isConstant()) {
|
||||
if (!arg->isConstantValue()) {
|
||||
callInfo.setImplicitlyUsedUnchecked();
|
||||
ArrayObject *templateArray = &templateObject->as<ArrayObject>();
|
||||
MNewArrayDynamicLength *ins =
|
||||
@ -389,7 +389,7 @@ IonBuilder::inlineArray(CallInfo &callInfo)
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
@ -1036,10 +1036,10 @@ IonBuilder::inlineMathPow(CallInfo &callInfo)
|
||||
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) {
|
||||
@ -1214,8 +1214,8 @@ IonBuilder::inlineMathMinMax(CallInfo &callInfo, bool max)
|
||||
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;
|
||||
@ -1366,14 +1366,14 @@ IonBuilder::inlineStrCharCodeAt(CallInfo &callInfo)
|
||||
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;
|
||||
@ -2031,9 +2031,9 @@ IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo)
|
||||
|
||||
// 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();
|
||||
|
||||
@ -2059,9 +2059,9 @@ IonBuilder::inlineUnsafeGetReservedSlot(CallInfo &callInfo)
|
||||
|
||||
// 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();
|
||||
|
||||
@ -2227,9 +2227,9 @@ IonBuilder::inlineAssertFloat32(CallInfo &callInfo)
|
||||
MDefinition *secondArg = callInfo.getArg(1);
|
||||
|
||||
MOZ_ASSERT(secondArg->type() == MIRType_Boolean);
|
||||
MOZ_ASSERT(secondArg->isConstant());
|
||||
MOZ_ASSERT(secondArg->isConstantValue());
|
||||
|
||||
bool mustBeFloat32 = secondArg->toConstant()->value().toBoolean();
|
||||
bool mustBeFloat32 = secondArg->constantValue().toBoolean();
|
||||
current->add(MAssertFloat32::New(alloc(), callInfo.getArg(0), mustBeFloat32));
|
||||
|
||||
MConstant *undefined = MConstant::New(alloc(), UndefinedValue());
|
||||
|
@ -71,17 +71,45 @@ MDefinition::PrintOpcodeName(FILE *fp, MDefinition::Opcode op)
|
||||
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()) {
|
||||
@ -146,10 +174,10 @@ EvaluateExactReciprocal(TempAllocator &alloc, MDiv *ins)
|
||||
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))
|
||||
@ -355,8 +383,8 @@ MTest::foldsTo(TempAllocator &alloc)
|
||||
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:
|
||||
@ -768,7 +796,7 @@ MSimdValueX4::foldsTo(TempAllocator &alloc)
|
||||
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;
|
||||
@ -783,14 +811,14 @@ MSimdValueX4::foldsTo(TempAllocator &alloc)
|
||||
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;
|
||||
}
|
||||
@ -809,7 +837,7 @@ 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);
|
||||
|
||||
@ -817,7 +845,7 @@ MSimdSplatX4::foldsTo(TempAllocator &alloc)
|
||||
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);
|
||||
@ -825,7 +853,7 @@ MSimdSplatX4::foldsTo(TempAllocator &alloc)
|
||||
}
|
||||
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);
|
||||
@ -1096,8 +1124,8 @@ MApplyArgs::New(TempAllocator &alloc, JSFunction *target, MDefinition *fun, MDef
|
||||
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()));
|
||||
}
|
||||
@ -1884,12 +1912,10 @@ 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 =
|
||||
@ -1959,25 +1985,25 @@ MDiv::analyzeEdgeCasesForward()
|
||||
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);
|
||||
}
|
||||
@ -2015,11 +2041,11 @@ MMod::analyzeEdgeCasesForward()
|
||||
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;
|
||||
}
|
||||
@ -2093,15 +2119,15 @@ MMul::analyzeEdgeCasesForward()
|
||||
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);
|
||||
}
|
||||
@ -2370,15 +2396,15 @@ MustBeUInt32(MDefinition *def, MDefinition **pwrapped)
|
||||
*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;
|
||||
@ -2553,7 +2579,7 @@ MBitNot::foldsTo(TempAllocator &alloc)
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2878,11 +2904,14 @@ 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));
|
||||
}
|
||||
@ -2893,12 +2922,15 @@ MTruncateToInt32::foldsTo(TempAllocator &alloc)
|
||||
MDefinition *
|
||||
MToDouble::foldsTo(TempAllocator &alloc)
|
||||
{
|
||||
MDefinition *in = input();
|
||||
if (in->type() == MIRType_Double)
|
||||
return in;
|
||||
MDefinition *input = getOperand(0);
|
||||
if (input->isBox())
|
||||
input = input->getOperand(0);
|
||||
|
||||
if (in->isConstant()) {
|
||||
const Value &v = in->toConstant()->value();
|
||||
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));
|
||||
@ -2911,15 +2943,19 @@ MToDouble::foldsTo(TempAllocator &alloc)
|
||||
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->isToDouble() && input->toToDouble()->input()->type() == MIRType_Float32)
|
||||
return input->toToDouble()->input();
|
||||
|
||||
if (input()->isConstant()) {
|
||||
const Value &v = input()->toConstant()->value();
|
||||
if (input->isConstant()) {
|
||||
const Value &v = input->toConstant()->value();
|
||||
if (v.isNumber()) {
|
||||
float out = v.toNumber();
|
||||
MConstant *c = MConstant::New(alloc, DoubleValue(out));
|
||||
@ -2934,6 +2970,9 @@ MDefinition *
|
||||
MToString::foldsTo(TempAllocator &alloc)
|
||||
{
|
||||
MDefinition *in = input();
|
||||
if (in->isBox())
|
||||
in = in->getOperand(0);
|
||||
|
||||
if (in->type() == MIRType_String)
|
||||
return in;
|
||||
return this;
|
||||
@ -2942,8 +2981,8 @@ MToString::foldsTo(TempAllocator &alloc)
|
||||
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));
|
||||
@ -3358,8 +3397,8 @@ 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));
|
||||
|
||||
@ -3925,8 +3964,8 @@ MGetPropertyCache::updateForReplacement(MDefinition *ins) {
|
||||
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())));
|
||||
}
|
||||
@ -3937,8 +3976,8 @@ MAsmJSUnsignedToDouble::foldsTo(TempAllocator &alloc)
|
||||
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))
|
||||
@ -3987,8 +4026,8 @@ MSqrt::trySpecializeFloat32(TempAllocator &alloc) {
|
||||
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)));
|
||||
@ -4000,9 +4039,9 @@ MClz::foldsTo(TempAllocator &alloc)
|
||||
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();
|
||||
}
|
||||
|
@ -752,6 +752,13 @@ class MDefinition : public MNode
|
||||
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 {
|
||||
|
@ -155,6 +155,9 @@ RangeAnalysis::addBetaNodes()
|
||||
|
||||
MCompare *compare = test->getOperand(0)->toCompare();
|
||||
|
||||
if (compare->compareType() == MCompare::Compare_Unknown)
|
||||
continue;
|
||||
|
||||
// TODO: support unsigned comparisons
|
||||
if (compare->compareType() == MCompare::Compare_UInt32)
|
||||
continue;
|
||||
@ -174,12 +177,12 @@ RangeAnalysis::addBetaNodes()
|
||||
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;
|
||||
@ -1310,14 +1313,14 @@ MLsh::computeRange(TempAllocator &alloc)
|
||||
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
|
||||
@ -1328,14 +1331,14 @@ MRsh::computeRange(TempAllocator &alloc)
|
||||
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
|
||||
@ -1353,11 +1356,11 @@ MUrsh::computeRange(TempAllocator &alloc)
|
||||
right.wrapAroundToShiftCount();
|
||||
|
||||
MDefinition *rhs = getOperand(1);
|
||||
if (!rhs->isConstant()) {
|
||||
setRange(Range::ursh(alloc, &left, &right));
|
||||
} else {
|
||||
int32_t c = rhs->toConstant()->value().toInt32();
|
||||
if (rhs->isConstantValue() && rhs->constantValue().isInt32()) {
|
||||
int32_t c = rhs->constantValue().toInt32();
|
||||
setRange(Range::ursh(alloc, &left, c));
|
||||
} else {
|
||||
setRange(Range::ursh(alloc, &left, &right));
|
||||
}
|
||||
|
||||
MOZ_ASSERT(range()->lower() >= 0);
|
||||
@ -2734,7 +2737,7 @@ CloneForDeadBranches(TempAllocator &alloc, MInstruction *candidate)
|
||||
|
||||
candidate->block()->insertBefore(candidate, clone);
|
||||
|
||||
if (!candidate->isConstant()) {
|
||||
if (!candidate->isConstantValue()) {
|
||||
MOZ_ASSERT(clone->canRecoverOnBailout());
|
||||
clone->setRecoveredOnBailout();
|
||||
}
|
||||
|
@ -533,10 +533,10 @@ IndexOf(MDefinition *ins, int32_t *res)
|
||||
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();
|
||||
@ -966,7 +966,7 @@ ArrayMemoryView::visitSetInitializedLength(MSetInitializedLength *ins)
|
||||
// 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_);
|
||||
|
@ -492,10 +492,10 @@ LIRGeneratorARM::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
|
||||
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);
|
||||
}
|
||||
@ -510,9 +510,9 @@ LIRGeneratorARM::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
|
||||
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);
|
||||
}
|
||||
|
@ -470,11 +470,11 @@ LIRGeneratorMIPS::visitAsmJSLoadHeap(MAsmJSLoadHeap *ins)
|
||||
|
||||
// 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);
|
||||
|
||||
@ -488,9 +488,9 @@ LIRGeneratorMIPS::visitAsmJSStoreHeap(MAsmJSStoreHeap *ins)
|
||||
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);
|
||||
|
||||
|
@ -127,6 +127,7 @@ BEGIN_TEST(testJitRangeAnalysis_MathSignBeta)
|
||||
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));
|
||||
|
||||
@ -144,6 +145,7 @@ BEGIN_TEST(testJitRangeAnalysis_MathSignBeta)
|
||||
// {
|
||||
// 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));
|
||||
|
||||
|
@ -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();
|
66
js/src/tests/ecma_7/SIMD/constructors.js
Normal file
66
js/src/tests/ecma_7/SIMD/constructors.js
Normal file
@ -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();
|
33
js/src/tests/ecma_7/SIMD/signmask.js
Normal file
33
js/src/tests/ecma_7/SIMD/signmask.js
Normal file
@ -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();
|
44
layout/generic/RubyUtils.cpp
Normal file
44
layout/generic/RubyUtils.cpp
Normal file
@ -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;
|
||||
}
|
63
layout/generic/RubyUtils.h
Normal file
63
layout/generic/RubyUtils.h
Normal file
@ -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_) */
|
@ -94,6 +94,7 @@ UNIFIED_SOURCES += [
|
||||
'nsTextRunTransformations.cpp',
|
||||
'nsVideoFrame.cpp',
|
||||
'nsViewportFrame.cpp',
|
||||
'RubyUtils.cpp',
|
||||
'ScrollbarActivity.cpp',
|
||||
'StickyScrollContainer.cpp',
|
||||
'TextOverflow.cpp',
|
||||
|
@ -762,8 +762,12 @@ public:
|
||||
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)
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsStyleStructInlines.h"
|
||||
#include "nsBidiPresUtils.h"
|
||||
#include "RubyUtils.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -77,7 +78,8 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
|
||||
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() ==
|
||||
@ -96,6 +98,9 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
|
||||
mTotalPlacedFrames = 0;
|
||||
mBStartEdge = 0;
|
||||
mTrimmableISize = 0;
|
||||
#ifdef DEBUG
|
||||
mFinalLineBSize = nscoord_MIN;
|
||||
#endif
|
||||
|
||||
mInflationMinFontSize =
|
||||
nsLayoutUtils::InflationMinFontSizeFor(aOuterReflowState->frame);
|
||||
@ -193,11 +198,6 @@ nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
|
||||
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() &
|
||||
@ -241,6 +241,11 @@ nsLineLayout::BeginLineReflow(nscoord aICoord, nscoord aBCoord,
|
||||
|
||||
psd->mICoord += indent;
|
||||
}
|
||||
|
||||
PerFrameData* pfd = NewPerFrameData(mBlockReflowState->frame);
|
||||
pfd->mAscent = 0;
|
||||
pfd->mSpan = psd;
|
||||
psd->mFrame = pfd;
|
||||
}
|
||||
|
||||
void
|
||||
@ -466,12 +471,12 @@ nsLineLayout::AttachFrameToBaseLineLayout(PerFrameData* aFrame)
|
||||
|
||||
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
|
||||
@ -559,7 +564,7 @@ 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.
|
||||
@ -646,9 +651,20 @@ nsLineLayout::NewPerFrameData(nsIFrame* aFrame)
|
||||
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);
|
||||
@ -856,9 +872,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
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);
|
||||
}
|
||||
@ -912,6 +928,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
}
|
||||
|
||||
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
|
||||
@ -923,7 +940,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
} 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
|
||||
@ -950,26 +967,25 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
}
|
||||
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();
|
||||
@ -978,7 +994,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
}
|
||||
}
|
||||
}
|
||||
pfd->SetFlag(PFD_ISEMPTY, isEmpty);
|
||||
pfd->mIsEmpty = isEmpty;
|
||||
|
||||
mFloatManager->Untranslate(oldWM, tPt);
|
||||
|
||||
@ -1053,7 +1069,7 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1074,6 +1090,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||
// nonempty leaf content has been placed
|
||||
mLineAtStart = false;
|
||||
}
|
||||
if (nsGkAtoms::rubyFrame == frameType) {
|
||||
mHasRuby = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Place the frame, updating aBounds with the final size and
|
||||
@ -1220,7 +1239,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
|
||||
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;
|
||||
@ -1395,7 +1414,7 @@ nsLineLayout::AddBulletFrame(nsIFrame* aFrame,
|
||||
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 {
|
||||
@ -1513,21 +1532,23 @@ nsLineLayout::VerticalAlignLine()
|
||||
}
|
||||
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;
|
||||
mLineBox->SetLogicalAscent(baselineBCoord - mBStartEdge);
|
||||
if (mGotLineBox) {
|
||||
// Fill in returned line-box and max-element-width data
|
||||
mLineBox->SetBounds(lineWM,
|
||||
psd->mIStart, mBStartEdge,
|
||||
psd->mICoord - psd->mIStart, lineBSize,
|
||||
mContainerWidth);
|
||||
|
||||
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.
|
||||
@ -1714,8 +1735,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
// 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;
|
||||
@ -1759,7 +1780,7 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
|
||||
// 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);
|
||||
@ -2069,11 +2090,11 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
// "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;
|
||||
}
|
||||
@ -2138,7 +2159,8 @@ nsLineLayout::VerticalAlignFrames(PerSpanData* psd)
|
||||
|
||||
// (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) {
|
||||
@ -2388,14 +2410,13 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
||||
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
|
||||
@ -2409,7 +2430,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
||||
#endif
|
||||
|
||||
if (trimOutput.mChanged) {
|
||||
pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, true);
|
||||
pfd->mRecomputeOverflow = true;
|
||||
}
|
||||
|
||||
// Delta width not being zero means that
|
||||
@ -2451,7 +2472,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@ -2475,12 +2496,22 @@ nsLineLayout::TrimTrailingWhiteSpace()
|
||||
|
||||
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,
|
||||
@ -2489,31 +2520,55 @@ nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD,
|
||||
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) {
|
||||
@ -2527,10 +2582,18 @@ nsLineLayout::ComputeFrameJustification(PerSpanData* aPSD,
|
||||
}
|
||||
|
||||
aState.mLastParticipant = pfd;
|
||||
aState.mCrossingRubyBaseBoundary = isRubyBase;
|
||||
}
|
||||
|
||||
if (firstChild) {
|
||||
outerOpportunities = extraOpportunities;
|
||||
firstChild = false;
|
||||
} else {
|
||||
innerOpportunities += extraOpportunities;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return outerOpportunities;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2539,18 +2602,38 @@ nsLineLayout::AdvanceAnnotationInlineBounds(PerFrameData* aPFD,
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2578,7 +2661,7 @@ nsLineLayout::ApplyLineJustificationToAnnotations(PerFrameData* aPFD,
|
||||
// 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;
|
||||
@ -2597,25 +2680,23 @@ nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD,
|
||||
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 {
|
||||
@ -2625,6 +2706,13 @@ nsLineLayout::ApplyFrameJustification(PerSpanData* aPSD,
|
||||
}
|
||||
|
||||
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;
|
||||
@ -2634,6 +2722,79 @@ nsLineLayout::ApplyFrameJustification(PerSpanData* 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
|
||||
@ -2678,18 +2839,38 @@ nsLineLayout::TextAlignLine(nsLineBox* aLine,
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
@ -2735,6 +2916,10 @@ nsLineLayout::TextAlignLine(nsLineBox* aLine,
|
||||
}
|
||||
}
|
||||
|
||||
if (mHasRuby) {
|
||||
ExpandInlineRubyBoxes(mRootSpan);
|
||||
}
|
||||
|
||||
if (mPresContext->BidiEnabled() &&
|
||||
(!mPresContext->IsVisualMode() || !lineWM.IsBidiLTR())) {
|
||||
nsBidiPresUtils::ReorderFrames(psd->mFirstFrame->mFrame,
|
||||
@ -2798,7 +2983,7 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflo
|
||||
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());
|
||||
@ -2829,12 +3014,12 @@ nsLineLayout::RelativePositionFrames(PerSpanData* psd, nsOverflowAreas& aOverflo
|
||||
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);
|
||||
|
@ -116,6 +116,15 @@ public:
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
@ -437,47 +446,27 @@ protected:
|
||||
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) {
|
||||
@ -499,7 +488,7 @@ protected:
|
||||
bool ParticipatesInJustification() const
|
||||
{
|
||||
// Skip bullets and empty frames
|
||||
return !GetFlag(PFD_ISBULLET) && !GetFlag(PFD_ISEMPTY);
|
||||
return !mIsBullet && !mIsEmpty;
|
||||
}
|
||||
};
|
||||
PerFrameData* mFrameFreeList;
|
||||
@ -601,6 +590,7 @@ protected:
|
||||
bool mHasBullet : 1;
|
||||
bool mDirtyNextLine : 1;
|
||||
bool mLineAtStart : 1;
|
||||
bool mHasRuby : 1;
|
||||
|
||||
int32_t mSpanDepth;
|
||||
#ifdef DEBUG
|
||||
@ -685,6 +675,14 @@ protected:
|
||||
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
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsStyleContext.h"
|
||||
#include "nsStyleStructInlines.h"
|
||||
#include "WritingModes.h"
|
||||
#include "RubyUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
@ -376,15 +377,17 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
// 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.
|
||||
@ -394,7 +397,6 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
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
|
||||
@ -407,8 +409,24 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
// 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;
|
||||
@ -568,6 +586,7 @@ nsRubyBaseContainerFrame::ReflowOnePair(nsPresContext* aPresContext,
|
||||
|
||||
nsReflowStatus reflowStatus;
|
||||
nsHTMLReflowMetrics metrics(*aReflowStates[i]);
|
||||
RubyUtils::ClearReservedISize(textFrame);
|
||||
|
||||
bool pushedFrame;
|
||||
aReflowStates[i]->mLineLayout->ReflowFrame(textFrame, reflowStatus,
|
||||
@ -590,6 +609,7 @@ nsRubyBaseContainerFrame::ReflowOnePair(nsPresContext* aPresContext,
|
||||
MOZ_ASSERT(aBaseFrame->GetType() == nsGkAtoms::rubyBaseFrame);
|
||||
nsReflowStatus reflowStatus;
|
||||
nsHTMLReflowMetrics metrics(aReflowState);
|
||||
RubyUtils::ClearReservedISize(aBaseFrame);
|
||||
|
||||
bool pushedFrame;
|
||||
aReflowState.mLineLayout->ReflowFrame(aBaseFrame, reflowStatus,
|
||||
@ -601,12 +621,24 @@ nsRubyBaseContainerFrame::ReflowOnePair(nsPresContext* aPresContext,
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
@ -271,6 +271,23 @@ nsRubyFrame::Reflow(nsPresContext* aPresContext,
|
||||
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,
|
||||
@ -356,6 +373,16 @@ nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
|
||||
}
|
||||
|
||||
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;
|
||||
@ -376,14 +403,37 @@ nsRubyFrame::ReflowSegment(nsPresContext* aPresContext,
|
||||
"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);
|
||||
|
@ -71,7 +71,8 @@ nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
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,
|
||||
@ -79,12 +80,5 @@ nsRubyTextContainerFrame::Reflow(nsPresContext* aPresContext,
|
||||
// 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);
|
||||
}
|
||||
|
@ -47,15 +47,16 @@ protected:
|
||||
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___ */
|
||||
|
@ -90,7 +90,10 @@ nsRubyTextFrame::Reflow(nsPresContext* aPresContext,
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1861,10 +1861,10 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||
|
||||
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) {
|
||||
@ -1896,7 +1896,8 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||
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();
|
||||
@ -2760,6 +2761,12 @@ static int32_t FindChar(const nsTextFragment* frag,
|
||||
|
||||
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;
|
||||
@ -8502,7 +8509,8 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
||||
// 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);
|
||||
|
@ -2,7 +2,7 @@ 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
|
||||
@ -22,5 +22,8 @@ fuzzy-if(winWidget,28,1) == dynamic-removal-3.html dynamic-removal-3-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
|
||||
|
32
layout/reftests/css-ruby/ruby-position-horizontal-ref.html
Normal file
32
layout/reftests/css-ruby/ruby-position-horizontal-ref.html
Normal file
@ -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>
|
28
layout/reftests/css-ruby/ruby-position-horizontal.html
Normal file
28
layout/reftests/css-ruby/ruby-position-horizontal.html
Normal file
@ -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>
|
33
layout/reftests/css-ruby/ruby-position-vertical-lr-ref.html
Normal file
33
layout/reftests/css-ruby/ruby-position-vertical-lr-ref.html
Normal file
@ -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>
|
29
layout/reftests/css-ruby/ruby-position-vertical-lr.html
Normal file
29
layout/reftests/css-ruby/ruby-position-vertical-lr.html
Normal file
@ -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>
|
33
layout/reftests/css-ruby/ruby-position-vertical-rl-ref.html
Normal file
33
layout/reftests/css-ruby/ruby-position-vertical-rl-ref.html
Normal file
@ -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>
|
29
layout/reftests/css-ruby/ruby-position-vertical-rl.html
Normal file
29
layout/reftests/css-ruby/ruby-position-vertical-rl.html
Normal file
@ -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>
|
24
layout/reftests/floats/1114329-ref.html
Normal file
24
layout/reftests/floats/1114329-ref.html
Normal file
@ -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>
|
30
layout/reftests/floats/1114329.html
Normal file
30
layout/reftests/floats/1114329.html
Normal file
@ -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>
|
@ -18,3 +18,4 @@ fails == 345369-2.html 345369-2-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
|
||||
|
@ -310,6 +310,7 @@ 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)
|
||||
@ -393,6 +394,7 @@ 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)
|
||||
@ -553,6 +555,7 @@ 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)
|
||||
|
@ -795,6 +795,7 @@ protected:
|
||||
bool ParseOverflow();
|
||||
bool ParsePadding();
|
||||
bool ParseQuotes();
|
||||
bool ParseRubyPosition(nsCSSValue& aValue);
|
||||
bool ParseSize();
|
||||
bool ParseTextAlign(nsCSSValue& aValue,
|
||||
const KTableValue aTable[]);
|
||||
@ -10009,6 +10010,8 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue,
|
||||
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:
|
||||
@ -13125,6 +13128,34 @@ CSSParserImpl::ParseQuotes()
|
||||
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()
|
||||
{
|
||||
|
@ -2976,6 +2976,17 @@ CSS_PROP_POSITION(
|
||||
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,
|
||||
|
@ -1625,6 +1625,16 @@ const KTableValue nsCSSProps::kResizeKTable[] = {
|
||||
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,
|
||||
|
@ -628,6 +628,7 @@ public:
|
||||
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[];
|
||||
|
@ -1290,6 +1290,13 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult,
|
||||
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);
|
||||
|
@ -3076,6 +3076,20 @@ nsComputedDOMStyle::DoGetLineHeight()
|
||||
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()
|
||||
{
|
||||
|
@ -374,6 +374,7 @@ private:
|
||||
|
||||
/* Text Properties */
|
||||
mozilla::dom::CSSValue* DoGetLineHeight();
|
||||
mozilla::dom::CSSValue* DoGetRubyPosition();
|
||||
mozilla::dom::CSSValue* DoGetTextAlign();
|
||||
mozilla::dom::CSSValue* DoGetTextAlignLast();
|
||||
mozilla::dom::CSSValue* DoGetTextCombineUpright();
|
||||
|
@ -197,6 +197,7 @@ 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)
|
||||
|
@ -4368,6 +4368,13 @@ nsRuleNode::ComputeTextData(void* aStartStruct,
|
||||
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,
|
||||
|
@ -843,6 +843,15 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
|
||||
#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
|
||||
|
@ -3344,6 +3344,7 @@ nsStyleText::nsStyleText(void)
|
||||
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;
|
||||
@ -3367,6 +3368,7 @@ nsStyleText::nsStyleText(const nsStyleText& aSource)
|
||||
mWordBreak(aSource.mWordBreak),
|
||||
mWordWrap(aSource.mWordWrap),
|
||||
mHyphens(aSource.mHyphens),
|
||||
mRubyPosition(aSource.mRubyPosition),
|
||||
mTextSizeAdjust(aSource.mTextSizeAdjust),
|
||||
mTextCombineUpright(aSource.mTextCombineUpright),
|
||||
mControlCharacterVisibility(aSource.mControlCharacterVisibility),
|
||||
@ -3407,6 +3409,7 @@ nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const
|
||||
(mWordBreak != aOther.mWordBreak) ||
|
||||
(mWordWrap != aOther.mWordWrap) ||
|
||||
(mHyphens != aOther.mHyphens) ||
|
||||
(mRubyPosition != aOther.mRubyPosition) ||
|
||||
(mTextSizeAdjust != aOther.mTextSizeAdjust) ||
|
||||
(mLetterSpacing != aOther.mLetterSpacing) ||
|
||||
(mLineHeight != aOther.mLineHeight) ||
|
||||
|
@ -1596,6 +1596,7 @@ struct nsStyleText {
|
||||
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
|
||||
|
@ -5047,6 +5047,21 @@ if (SpecialPowers.getBoolPref("layout.css.ruby.enabled")) {
|
||||
"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")) {
|
||||
|
356
media/libstagefright/binding/H264.cpp
Normal file
356
media/libstagefright/binding/H264.cpp
Normal file
@ -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
|
361
media/libstagefright/binding/include/mp4_demuxer/H264.h
Normal file
361
media/libstagefright/binding/include/mp4_demuxer/H264.h
Normal file
@ -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_
|
@ -55,6 +55,7 @@ EXPORTS.mp4_demuxer += [
|
||||
'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',
|
||||
@ -75,6 +76,7 @@ UNIFIED_SOURCES += [
|
||||
'binding/Box.cpp',
|
||||
'binding/BufferStream.cpp',
|
||||
'binding/DecoderData.cpp',
|
||||
'binding/H264.cpp',
|
||||
'binding/Index.cpp',
|
||||
'binding/MoofParser.cpp',
|
||||
'binding/mp4_demuxer.cpp',
|
||||
|
@ -83,7 +83,7 @@ VideoContentMetrics* VPMContentAnalysis::ComputeContentMetrics(
|
||||
|
||||
int32_t VPMContentAnalysis::Release() {
|
||||
content_metrics_.reset(NULL);
|
||||
prev_frame_.reset(NULL);
|
||||
prev_frame_.reset();
|
||||
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
@ -112,7 +112,7 @@ int32_t VPMContentAnalysis::Initialize(int width, int height) {
|
||||
}
|
||||
|
||||
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.
|
||||
|
@ -66,7 +66,7 @@ class VPMContentAnalysis {
|
||||
#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_;
|
||||
|
@ -1 +0,0 @@
|
||||
../../../../tools/perf
|
@ -512,11 +512,11 @@
|
||||
* 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.
|
||||
@ -535,8 +535,8 @@
|
||||
# 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
|
||||
@ -556,8 +556,8 @@
|
||||
# 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 */
|
||||
|
||||
/*
|
||||
|
@ -79,6 +79,11 @@ struct UnsignedStdintTypeForSize
|
||||
: detail::StdintTypeForSizeAndSignedness<Size, false>
|
||||
{};
|
||||
|
||||
template<size_t Size>
|
||||
struct SignedStdintTypeForSize
|
||||
: detail::StdintTypeForSizeAndSignedness<Size, true>
|
||||
{};
|
||||
|
||||
template<typename IntegerType>
|
||||
struct PositionOfSignBit
|
||||
{
|
||||
|
@ -285,7 +285,7 @@ private:
|
||||
mPtr = aVal;
|
||||
}
|
||||
|
||||
T* mPtr;
|
||||
T* MOZ_OWNING_REF mPtr;
|
||||
|
||||
static MOZ_ALWAYS_INLINE T* ref(T* aVal)
|
||||
{
|
||||
@ -336,7 +336,7 @@ public:
|
||||
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;
|
||||
|
@ -69,6 +69,7 @@
|
||||
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/NullPtr.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
@ -122,7 +123,7 @@ private:
|
||||
|
||||
void detach() { mPtr = nullptr; }
|
||||
|
||||
T* mPtr;
|
||||
T* MOZ_NON_OWNING_REF mPtr;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
@ -25,7 +25,6 @@ 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);
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import itertools
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@ -16,6 +17,7 @@ from ..frontend.data import (
|
||||
ConfigFileSubstitution,
|
||||
ExampleWebIDLInterface,
|
||||
HeaderFileSubstitution,
|
||||
IPDLFile,
|
||||
GeneratedEventWebIDLFile,
|
||||
GeneratedWebIDLFile,
|
||||
PreprocessedTestWebIDLFile,
|
||||
@ -172,6 +174,7 @@ class CommonBackend(BuildBackend):
|
||||
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)
|
||||
@ -225,6 +228,9 @@ class CommonBackend(BuildBackend):
|
||||
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
|
||||
|
||||
@ -236,6 +242,24 @@ class CommonBackend(BuildBackend):
|
||||
|
||||
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)
|
||||
|
||||
|
@ -45,7 +45,6 @@ from ..frontend.data import (
|
||||
HostSimpleProgram,
|
||||
HostSources,
|
||||
InstallationTarget,
|
||||
IPDLFile,
|
||||
JARManifest,
|
||||
JavaJarData,
|
||||
JavaScriptModules,
|
||||
@ -277,7 +276,6 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
CommonBackend._init(self)
|
||||
|
||||
self._backend_files = {}
|
||||
self._ipdl_sources = set()
|
||||
|
||||
def detailed(summary):
|
||||
s = '{:d} total backend files; ' \
|
||||
@ -451,9 +449,6 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
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)
|
||||
@ -750,36 +745,6 @@ class RecursiveMakeBackend(CommonBackend):
|
||||
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
|
||||
@ -1332,6 +1297,23 @@ INSTALL_TARGETS += %(prefix)s
|
||||
|
||||
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
|
||||
|
@ -101,16 +101,16 @@ class VisualStudioBackend(CommonBackend):
|
||||
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
|
||||
|
@ -1109,7 +1109,7 @@ AuthCertificate(CertVerifier& certVerifier,
|
||||
RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
|
||||
RefPtr<nsNSSCertificate> nsc;
|
||||
|
||||
if (!status || !status->mServerCert) {
|
||||
if (!status || !status->HasServerCert()) {
|
||||
if( rv == SECSuccess ){
|
||||
nsc = nsNSSCertificate::Create(cert, &evOidPolicy);
|
||||
}
|
||||
@ -1141,10 +1141,17 @@ AuthCertificate(CertVerifier& certVerifier,
|
||||
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()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -242,11 +242,11 @@ TransportSecurityInfo::formatErrorMessage(MutexAutoLock const & proofOfLock,
|
||||
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,
|
||||
@ -296,8 +296,8 @@ TransportSecurityInfo::GetInterface(const nsIID & uuid, void * *result)
|
||||
// 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
|
||||
@ -1089,7 +1089,7 @@ TransportSecurityInfo::SetStatusErrorBits(nsIX509Cert & cert,
|
||||
if (!mSSLStatus)
|
||||
mSSLStatus = new nsSSLStatus();
|
||||
|
||||
mSSLStatus->mServerCert = &cert;
|
||||
mSSLStatus->SetServerCert(&cert, nsNSSCertificate::ev_status_invalid);
|
||||
|
||||
mSSLStatus->mHaveCertErrorBits = true;
|
||||
mSSLStatus->mIsDomainMismatch =
|
||||
|
@ -1288,17 +1288,17 @@ void HandshakeCallback(PRFileDesc* fd, void* client_data) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,6 +49,12 @@ public:
|
||||
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();
|
||||
|
||||
@ -65,9 +71,7 @@ private:
|
||||
|
||||
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);
|
||||
|
@ -650,9 +650,6 @@ static const CipherPref sCipherPrefs[] = {
|
||||
{ "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",
|
||||
|
@ -398,8 +398,9 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
|
||||
|
||||
// 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
|
||||
@ -418,7 +419,10 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -121,28 +121,20 @@ 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
|
||||
}
|
||||
|
||||
@ -169,7 +161,11 @@ nsSSLStatus::Read(nsIObjectInputStream* aStream)
|
||||
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);
|
||||
@ -197,7 +193,11 @@ nsSSLStatus::Write(nsIObjectOutputStream* aStream)
|
||||
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);
|
||||
@ -274,6 +274,8 @@ nsSSLStatus::nsSSLStatus()
|
||||
, mIsDomainMismatch(false)
|
||||
, mIsNotValidAtThisTime(false)
|
||||
, mIsUntrusted(false)
|
||||
, mIsEV(false)
|
||||
, mHasIsEVStatus(false)
|
||||
, mHaveCipherSuiteAndProtocol(false)
|
||||
, mHaveCertErrorBits(false)
|
||||
{
|
||||
@ -284,3 +286,26 @@ 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
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsIX509Cert.h"
|
||||
#include "nsISerializable.h"
|
||||
#include "nsIClassInfo.h"
|
||||
#include "nsNSSCertificate.h" // For EVStatus
|
||||
|
||||
class nsSSLStatus MOZ_FINAL
|
||||
: public nsISSLStatus
|
||||
@ -29,25 +30,34 @@ public:
|
||||
|
||||
nsSSLStatus();
|
||||
|
||||
/* public for initilization in this file */
|
||||
nsCOMPtr<nsIX509Cert> mServerCert;
|
||||
void SetServerCert(nsIX509Cert* aServerCert, nsNSSCertificate::EVStatus aEVStatus);
|
||||
|
||||
bool HasServerCert() {
|
||||
return mServerCert != nullptr;
|
||||
}
|
||||
|
||||
/* public for initilization in this file */
|
||||
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
|
||||
|
@ -31,7 +31,7 @@ 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:
|
||||
@ -73,28 +73,28 @@ protected:
|
||||
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)
|
||||
{
|
||||
|
@ -74,11 +74,11 @@ 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*/;
|
||||
};
|
||||
@ -87,11 +87,11 @@ 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*/;
|
||||
@ -101,12 +101,12 @@ 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;
|
||||
@ -118,12 +118,12 @@ template <typename R, typename P1, typename B1, typename B2, typename B3,
|
||||
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;
|
||||
@ -136,12 +136,18 @@ template <typename R, typename C1, typename P1, typename P2, typename P3,
|
||||
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*/;
|
||||
};
|
||||
|
||||
@ -150,12 +156,12 @@ template <typename R, typename P1, typename B1, typename B2, typename B3,
|
||||
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;
|
||||
@ -168,21 +174,21 @@ private:
|
||||
|
||||
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);
|
||||
@ -191,17 +197,20 @@ bind(R (*f)(P1&, B1, B2, B3&), Placeholder1&, const B1& b1, const B2& b2,
|
||||
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);
|
||||
}
|
||||
@ -209,7 +218,7 @@ bind(R (C1::*f)(P1&, P2&, P3, P4&), C1* that, Placeholder1&, Placeholder2&,
|
||||
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);
|
||||
|
@ -141,7 +141,7 @@ BackCert::Init()
|
||||
}
|
||||
|
||||
rv = der::OptionalExtensions(tbsCertificate, CSC | 3,
|
||||
bind(&BackCert::RememberExtension, this, _1,
|
||||
bind(&BackCert::RememberExtension, *this, _1,
|
||||
_2, _3, _4));
|
||||
if (rv != Success) {
|
||||
return rv;
|
||||
|
@ -22,25 +22,21 @@
|
||||
* 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;
|
||||
@ -65,16 +61,11 @@ CreateCert(const char* issuerCN,
|
||||
*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;
|
||||
}
|
||||
|
||||
@ -90,66 +81,53 @@ public:
|
||||
"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;
|
||||
}
|
||||
|
||||
@ -168,8 +146,7 @@ private:
|
||||
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,
|
||||
@ -184,15 +161,9 @@ private:
|
||||
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
|
||||
@ -200,12 +171,6 @@ 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();
|
||||
}
|
||||
@ -221,9 +186,9 @@ protected:
|
||||
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,
|
||||
@ -234,7 +199,6 @@ TEST_F(pkixbuild, MaxAcceptableCertChainLength)
|
||||
}
|
||||
|
||||
{
|
||||
ScopedCERTCertificate cert;
|
||||
ByteString certDER(CreateCert("CA7", "Direct End-Entity",
|
||||
EndEntityOrCA::MustBeEndEntity));
|
||||
ASSERT_FALSE(ENCODING_FAILED(certDER));
|
||||
@ -254,14 +218,10 @@ 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,
|
||||
@ -351,8 +311,7 @@ public:
|
||||
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)
|
||||
|
@ -4,7 +4,6 @@
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
#include "pkix/pkix.h"
|
||||
#include "pkix/pkixnss.h"
|
||||
#include "pkixgtest.h"
|
||||
#include "pkixtestutil.h"
|
||||
|
||||
@ -110,8 +109,7 @@ private:
|
||||
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)
|
||||
|
@ -83,6 +83,20 @@ InputEqualsByteString(Input input, const ByteString& bs)
|
||||
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)
|
||||
|
@ -67,6 +67,7 @@ public:
|
||||
};
|
||||
|
||||
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[] = {
|
||||
|
@ -338,10 +338,12 @@ function AbstractHealthReporter(branch, policy, sessionRecorder) {
|
||||
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({
|
||||
@ -415,6 +417,7 @@ AbstractHealthReporter.prototype = Object.freeze({
|
||||
shutdownComplete: this._shutdownComplete,
|
||||
currentProviderInShutdown: this._currentProviderInShutdown,
|
||||
currentProviderInInit: this._currentProviderInInit,
|
||||
currentProviderInCollect: this._currentProviderInCollect,
|
||||
}));
|
||||
|
||||
try {
|
||||
@ -793,7 +796,8 @@ AbstractHealthReporter.prototype = Object.freeze({
|
||||
|
||||
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);
|
||||
@ -813,7 +817,8 @@ AbstractHealthReporter.prototype = Object.freeze({
|
||||
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);
|
||||
|
@ -436,8 +436,11 @@ this.ProviderManager.prototype = Object.freeze({
|
||||
* 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) {
|
||||
@ -455,18 +458,20 @@ this.ProviderManager.prototype = Object.freeze({
|
||||
};
|
||||
|
||||
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) {
|
||||
@ -502,7 +507,7 @@ this.ProviderManager.prototype = Object.freeze({
|
||||
promises.push([provider.name, promise]);
|
||||
}
|
||||
|
||||
return this._handleCollectionPromises(promises);
|
||||
return this._handleCollectionPromises(promises, providerDiagnostic);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -514,9 +519,13 @@ this.ProviderManager.prototype = Object.freeze({
|
||||
* 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);
|
||||
|
@ -9,6 +9,7 @@ config = {
|
||||
"--symbols-path=%(symbols_path)s",
|
||||
"--xre-path=%(abs_app_dir)s"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "runcppunittests.py",
|
||||
"testsdir": "cppunittests"
|
||||
},
|
||||
@ -20,6 +21,7 @@ config = {
|
||||
"--tinderbox",
|
||||
"--tbpl"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "jit_test.py",
|
||||
"testsdir": "jit-test/jit-test"
|
||||
},
|
||||
@ -36,6 +38,7 @@ config = {
|
||||
"--quiet",
|
||||
"--log-raw=%(raw_log_file)s"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "runtests.py",
|
||||
"testsdir": "mochitest"
|
||||
},
|
||||
@ -44,6 +47,7 @@ config = {
|
||||
"-b",
|
||||
"%(binary_path)s"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "test.py",
|
||||
"testsdir": "mozbase"
|
||||
},
|
||||
@ -54,6 +58,7 @@ config = {
|
||||
"--extra-profile-file=tests/bin/plugins",
|
||||
"--symbols-path=%(symbols_path)s"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "runreftest.py",
|
||||
"testsdir": "reftest"
|
||||
},
|
||||
@ -70,6 +75,7 @@ config = {
|
||||
"--testing-modules-dir=tests/modules",
|
||||
"--quiet"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "runtests.py",
|
||||
"testsdir": "mochitest"
|
||||
},
|
||||
@ -79,8 +85,9 @@ config = {
|
||||
"--test-plugin-path=%(test_plugin_path)s",
|
||||
"--log-raw=%(raw_log_file)s"
|
||||
],
|
||||
"mac_res_subdir": "MacOS",
|
||||
"run_filename": "runxpcshelltests.py",
|
||||
"testsdir": "xpcshell"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ 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;
|
||||
@ -86,6 +87,11 @@ if (outOfProcess) {
|
||||
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"]
|
||||
@ -133,5 +139,7 @@ if (onDevice) {
|
||||
};
|
||||
}
|
||||
} else {
|
||||
container.src = mochitestUrl;
|
||||
if (!chrome) {
|
||||
container.src = mochitestUrl;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ var gConfig;
|
||||
|
||||
if (Cc === undefined) {
|
||||
var Cc = Components.classes;
|
||||
}
|
||||
if (Ci === undefined) {
|
||||
var Ci = Components.interfaces;
|
||||
}
|
||||
if (Cu === undefined) {
|
||||
var Cu = Components.utils;
|
||||
}
|
||||
|
||||
@ -32,6 +36,15 @@ window.addEventListener("load", function testOnLoad() {
|
||||
});
|
||||
});
|
||||
|
||||
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" ||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user