зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound.
This commit is contained in:
Коммит
4258150825
|
@ -903,6 +903,7 @@ pref("apz.y_stationary_size_multiplier", "1.8");
|
|||
pref("apz.enlarge_displayport_when_clipped", true);
|
||||
// Use "sticky" axis locking
|
||||
pref("apz.axis_lock_mode", 2);
|
||||
pref("apz.subframe.enabled", true);
|
||||
|
||||
// This preference allows FirefoxOS apps (and content, I think) to force
|
||||
// the use of software (instead of hardware accelerated) 2D canvases by
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
. "$topsrcdir/b2g/config/mozconfigs/common"
|
||||
. "$topsrcdir/build/unix/mozconfig.linux"
|
||||
|
||||
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
|
||||
ac_add_options --enable-update-packaging
|
||||
ac_add_options --enable-signmar
|
||||
ac_add_options --enable-debug
|
||||
|
||||
# Nightlies only since this has a cost in performance
|
||||
#ac_add_options --enable-js-diagnostics
|
||||
|
||||
# This will overwrite the default of stripping everything and keep the symbol table.
|
||||
# This is useful for profiling and debugging and only increases the package size
|
||||
# by 2 MBs.
|
||||
STRIP_FLAGS="--strip-debug"
|
||||
|
||||
# Needed to enable breakpad in application.ini
|
||||
export MOZILLA_OFFICIAL=1
|
||||
|
||||
export MOZ_TELEMETRY_REPORTING=1
|
||||
|
||||
# Treat warnings as errors in directories with FAIL_ON_WARNINGS.
|
||||
# DISABLED WHILE NOT ON TRY ac_add_options --enable-warnings-as-errors
|
||||
|
||||
# Use ccache
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
||||
#B2G options
|
||||
ac_add_options --enable-application=b2g
|
||||
ENABLE_MARIONETTE=1
|
||||
ac_add_options --disable-elf-hack
|
||||
export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
|
||||
|
||||
GAIADIR=$topsrcdir/gaia
|
||||
|
||||
. "$topsrcdir/b2g/config/mozconfigs/common.override"
|
|
@ -58,6 +58,7 @@ pref("apz.min_skate_speed", "10.0");
|
|||
// 0 = free, 1 = standard, 2 = sticky
|
||||
pref("apz.axis_lock_mode", 2);
|
||||
pref("apz.cross_slide.enabled", true);
|
||||
pref("apz.subframe.enabled", true);
|
||||
|
||||
// Enable Microsoft TSF support by default for imes.
|
||||
pref("intl.tsf.enable", true);
|
||||
|
|
|
@ -109,6 +109,7 @@ void
|
|||
TextTrack::AddCue(TextTrackCue& aCue)
|
||||
{
|
||||
mCueList->AddCue(aCue);
|
||||
aCue.SetTrack(this);
|
||||
if (mTextTrackList) {
|
||||
HTMLMediaElement* mediaElement = mTextTrackList->GetMediaElement();
|
||||
if (mediaElement) {
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
#include "nsIDocument.h"
|
||||
#include "mozilla/dom/HTMLDivElement.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "mozilla/dom/TextTrack.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class HTMLTrackElement;
|
||||
class TextTrack;
|
||||
class TextTrackRegion;
|
||||
|
||||
class TextTrackCue MOZ_FINAL : public DOMEventTargetHelper
|
||||
|
@ -317,6 +317,11 @@ public:
|
|||
return mId;
|
||||
}
|
||||
|
||||
void SetTrack(TextTrack* aTextTrack)
|
||||
{
|
||||
mTrack = aTextTrack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a tree of anonymous content based on the tree of the processed
|
||||
* cue text.
|
||||
|
|
|
@ -53,9 +53,10 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
|
|||
is(cue.endTime, 0.7, "Cue's end time should be 0.7.");
|
||||
is(cue.pauseOnExit, false, "Cue's pause on exit flag should be false.");
|
||||
is(cue.text, "This", "Cue's text should be set correctly.");
|
||||
// TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=879431
|
||||
// GetTrack() isn't implemented so this *is* returning undefined currently.
|
||||
todo_isnot(cue.track, undefined, "Cue's track should be defined.");
|
||||
is(cue.track, trackElement.track, "Cue's track should be defined.");
|
||||
|
||||
cue.track = null;
|
||||
isnot(cue.track, null, "Cue's track should not be able to be set.");
|
||||
|
||||
// Check that all cue times were not rounded
|
||||
is(cueList[1].startTime, 1.2, "Second cue's start time should be 1.2.");
|
||||
|
@ -184,7 +185,9 @@ SpecialPowers.pushPrefEnv({"set": [["media.webvtt.enabled", true],
|
|||
|
||||
// Check that we can create and add new VTTCues
|
||||
var vttCue = new VTTCue(3.999, 4, "foo");
|
||||
is(vttCue.track, undefined, "Cue's track should be undefined.");
|
||||
trackElement.track.addCue(vttCue);
|
||||
is(cue.track, trackElement.track, "Cue's track should be defined.");
|
||||
is(cueList.length, 7, "Cue list length should now be 7.");
|
||||
|
||||
// Check that new VTTCue was added correctly
|
||||
|
|
|
@ -1374,7 +1374,8 @@ class CGConstructNavigatorObject(CGAbstractMethod):
|
|||
}
|
||||
JS::Rooted<JS::Value> v(aCx);
|
||||
if (!WrapNewBindingObject(aCx, aObj, result, &v)) {
|
||||
MOZ_ASSERT(JS_IsExceptionPending(aCx));
|
||||
//XXX Assertion disabled for now, see bug 991271.
|
||||
MOZ_ASSERT(true || JS_IsExceptionPending(aCx));
|
||||
return nullptr;
|
||||
}
|
||||
return &v.toObject();"""
|
||||
|
@ -4729,7 +4730,8 @@ if (!returnArray) {
|
|||
if not descriptor.hasXPConnectImpls:
|
||||
# Can only fail to wrap as a new-binding object
|
||||
# if they already threw an exception.
|
||||
failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
|
||||
#XXX Assertion disabled for now, see bug 991271.
|
||||
failed = ("MOZ_ASSERT(true || JS_IsExceptionPending(cx));\n" +
|
||||
"%s" % exceptionCode)
|
||||
else:
|
||||
if descriptor.notflattened:
|
||||
|
@ -11267,7 +11269,8 @@ nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
|
|||
JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
|
||||
JS::Rooted<JS::Value> wrappedVal(cx);
|
||||
if (!WrapNewBindingObject(cx, scopeObj, impl, &wrappedVal)) {
|
||||
MOZ_ASSERT(JS_IsExceptionPending(cx));
|
||||
//XXX Assertion disabled for now, see bug 991271.
|
||||
MOZ_ASSERT(true || JS_IsExceptionPending(cx));
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ private:
|
|||
DECL_GFX_PREF(Live, "apz.min_skate_speed", APZMinSkateSpeed, float, 1.0f);
|
||||
DECL_GFX_PREF(Live, "apz.num_paint_duration_samples", APZNumPaintDurationSamples, int32_t, 3);
|
||||
DECL_GFX_PREF(Live, "apz.pan_repaint_interval", APZPanRepaintInterval, int32_t, 250);
|
||||
DECL_GFX_PREF(Live, "apz.subframe.enabled", APZSubframeEnabled, bool, false);
|
||||
DECL_GFX_PREF(Live, "apz.touch_start_tolerance", APZTouchStartTolerance, float, 1.0f/4.5f);
|
||||
DECL_GFX_PREF(Live, "apz.use_paint_duration", APZUsePaintDuration, bool, true);
|
||||
DECL_GFX_PREF(Live, "apz.velocity_bias", APZVelocityBias, float, 1.0f);
|
||||
|
|
|
@ -401,7 +401,6 @@ static nsresult NewImageChannel(nsIChannel **aResult,
|
|||
nsIPrincipal *aLoadingPrincipal)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIChannel> newChannel;
|
||||
nsCOMPtr<nsIHttpChannel> newHttpChannel;
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks;
|
||||
|
@ -463,6 +462,18 @@ static nsresult NewImageChannel(nsIChannel **aResult,
|
|||
*aResult, aURI, false);
|
||||
*aForcePrincipalCheckForCacheEntry = setOwner;
|
||||
|
||||
// Create a new loadgroup for this new channel, using the old group as
|
||||
// the parent. The indirection keeps the channel insulated from cancels,
|
||||
// but does allow a way for this revalidation to be associated with at
|
||||
// least one base load group for scheduling/caching purposes.
|
||||
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
|
||||
nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
|
||||
if (childLoadGroup) {
|
||||
childLoadGroup->SetParentLoadGroup(aLoadGroup);
|
||||
}
|
||||
(*aResult)->SetLoadGroup(loadGroup);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1679,18 +1690,9 @@ nsresult imgLoader::LoadImage(nsIURI *aURI,
|
|||
PR_LOG(GetImgLog(), PR_LOG_DEBUG,
|
||||
("[this=%p] imgLoader::LoadImage -- Created new imgRequest [request=%p]\n", this, request.get()));
|
||||
|
||||
// Create a loadgroup for this new channel. This way if the channel
|
||||
// is redirected, we'll have a way to cancel the resulting channel.
|
||||
// Inform the new loadgroup of the old one so they can still be correlated
|
||||
// together as a logical group.
|
||||
nsCOMPtr<nsILoadGroup> loadGroup =
|
||||
do_CreateInstance(NS_LOADGROUP_CONTRACTID);
|
||||
nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
|
||||
if (childLoadGroup)
|
||||
childLoadGroup->SetParentLoadGroup(aLoadGroup);
|
||||
newChannel->SetLoadGroup(loadGroup);
|
||||
|
||||
request->Init(aURI, aURI, loadGroup, newChannel, entry, aCX,
|
||||
nsCOMPtr<nsILoadGroup> channelLoadGroup;
|
||||
newChannel->GetLoadGroup(getter_AddRefs(channelLoadGroup));
|
||||
request->Init(aURI, aURI, channelLoadGroup, newChannel, entry, aCX,
|
||||
aLoadingPrincipal, corsmode);
|
||||
|
||||
// Pass the inner window ID of the loading document, if possible.
|
||||
|
|
|
@ -1257,8 +1257,11 @@ Parser<ParseHandler>::newFunction(GenericParseContext *pc, HandleAtom atom,
|
|||
: (kind == Arrow)
|
||||
? JSFunction::INTERPRETED_LAMBDA_ARROW
|
||||
: JSFunction::INTERPRETED;
|
||||
gc::AllocKind allocKind = (kind == Arrow)
|
||||
? JSFunction::ExtendedFinalizeKind
|
||||
: JSFunction::FinalizeKind;
|
||||
fun = NewFunctionWithProto(context, NullPtr(), nullptr, 0, flags, NullPtr(), atom, proto,
|
||||
JSFunction::FinalizeKind, MaybeSingletonObject);
|
||||
allocKind, MaybeSingletonObject);
|
||||
if (!fun)
|
||||
return nullptr;
|
||||
if (options().selfHostingMode)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
var f = x => arguments.callee;
|
||||
|
||||
for (var i=0; i<5; i++)
|
||||
assertEq(f(), f);
|
|
@ -7,5 +7,5 @@ assertEq((() => 0).length, 0);
|
|||
assertEq(((a) => 0).length, 1);
|
||||
assertEq(((a, b) => 0).length, 2);
|
||||
|
||||
assertEq(((...arr) => arr).length, 1);
|
||||
assertEq(((a=1, b=2) => 0).length, 2);
|
||||
assertEq(((...arr) => arr).length, 0);
|
||||
assertEq(((a=1, b=2) => 0).length, 0);
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Arrow functions can have primitive |this| values.
|
||||
|
||||
Number.prototype.foo = function() {
|
||||
"use strict";
|
||||
return () => this;
|
||||
}
|
||||
|
||||
for (var i=0; i<5; i++) {
|
||||
var n = i.foo()();
|
||||
assertEq(typeof n, "number");
|
||||
assertEq(n, i);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
// Eval expressions in arrow functions use the correct |this| value.
|
||||
|
||||
function Dog(name) {
|
||||
this.name = name;
|
||||
this.getName = () => eval("this.name");
|
||||
this.getNameHard = () => eval("(() => this.name)()");
|
||||
}
|
||||
|
||||
var d = new Dog("Max");
|
||||
assertEq(d.getName(), d.name);
|
||||
assertEq(d.getNameHard(), d.name);
|
|
@ -0,0 +1,15 @@
|
|||
function getTestCaseResult(expected, actual) {
|
||||
if (actual != expected)
|
||||
return Math.abs(actual - expected) <= 1E-10;
|
||||
}
|
||||
function InstanceOf(object, constructor) {
|
||||
while ( object != null )
|
||||
object = object.__proto__;
|
||||
}
|
||||
function WorkerBee () {}
|
||||
function Engineer () {}
|
||||
Engineer.prototype = new WorkerBee();
|
||||
var pat = new Engineer();
|
||||
getTestCaseResult(pat.__proto__.__proto__.__proto__.__proto__ == Object.prototype);
|
||||
getTestCaseResult(InstanceOf(pat, Engineer));
|
||||
evaluate("getTestCaseResult( Object.prototype.__proto__ );", { compileAndGo: true });
|
|
@ -1103,6 +1103,17 @@ BaselineCompiler::emit_JSOP_NULL()
|
|||
bool
|
||||
BaselineCompiler::emit_JSOP_THIS()
|
||||
{
|
||||
if (function() && function()->isArrow()) {
|
||||
// Arrow functions store their (lexical) |this| value in an
|
||||
// extended slot.
|
||||
frame.syncStack(0);
|
||||
Register scratch = R0.scratchReg();
|
||||
masm.loadPtr(frame.addressOfCallee(), scratch);
|
||||
masm.loadValue(Address(scratch, FunctionExtended::offsetOfArrowThisSlot()), R0);
|
||||
frame.push(R0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Keep this value in R0
|
||||
frame.pushThis();
|
||||
|
||||
|
|
|
@ -4061,6 +4061,15 @@ CodeGenerator::visitComputeThis(LComputeThis *lir)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitLoadArrowThis(LLoadArrowThis *lir)
|
||||
{
|
||||
Register callee = ToRegister(lir->callee());
|
||||
ValueOperand output = ToOutValue(lir);
|
||||
masm.loadValue(Address(callee, FunctionExtended::offsetOfArrowThisSlot()), output);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CodeGenerator::visitArrayLength(LArrayLength *lir)
|
||||
{
|
||||
|
|
|
@ -160,6 +160,7 @@ class CodeGenerator : public CodeGeneratorSpecific
|
|||
bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir);
|
||||
bool visitReturnFromCtor(LReturnFromCtor *lir);
|
||||
bool visitComputeThis(LComputeThis *lir);
|
||||
bool visitLoadArrowThis(LLoadArrowThis *lir);
|
||||
bool visitArrayLength(LArrayLength *lir);
|
||||
bool visitSetArrayLength(LSetArrayLength *lir);
|
||||
bool visitTypedArrayLength(LTypedArrayLength *lir);
|
||||
|
|
|
@ -1657,18 +1657,10 @@ IonBuilder::inspectOpcode(JSOp op)
|
|||
case JSOP_THIS:
|
||||
return jsop_this();
|
||||
|
||||
case JSOP_CALLEE:
|
||||
{
|
||||
MDefinition *callee;
|
||||
if (inliningDepth_ == 0) {
|
||||
MInstruction *calleeIns = MCallee::New(alloc());
|
||||
current->add(calleeIns);
|
||||
callee = calleeIns;
|
||||
} else {
|
||||
callee = inlineCallInfo_->fun();
|
||||
}
|
||||
current->push(callee);
|
||||
return true;
|
||||
case JSOP_CALLEE: {
|
||||
MDefinition *callee = getCallee();
|
||||
current->push(callee);
|
||||
return true;
|
||||
}
|
||||
|
||||
case JSOP_GETPROP:
|
||||
|
@ -3032,7 +3024,7 @@ IonBuilder::filterTypesAtTest(MTest *test)
|
|||
return true;
|
||||
|
||||
// There is no TypeSet that can get filtered.
|
||||
if (!subject->resultTypeSet())
|
||||
if (!subject->resultTypeSet() || subject->resultTypeSet()->unknown())
|
||||
return true;
|
||||
|
||||
// Only do this optimization if the typeset does contains null or undefined.
|
||||
|
@ -9534,6 +9526,14 @@ IonBuilder::jsop_this()
|
|||
if (!info().funMaybeLazy())
|
||||
return abort("JSOP_THIS outside of a JSFunction.");
|
||||
|
||||
if (info().funMaybeLazy()->isArrow()) {
|
||||
// Arrow functions store their lexical |this| in an extended slot.
|
||||
MLoadArrowThis *thisObj = MLoadArrowThis::New(alloc(), getCallee());
|
||||
current->add(thisObj);
|
||||
current->push(thisObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (script()->strict() || info().funMaybeLazy()->isSelfHostedBuiltin()) {
|
||||
// No need to wrap primitive |this| in strict mode or self-hosted code.
|
||||
current->pushSlot(info().thisSlot());
|
||||
|
@ -10230,3 +10230,15 @@ IonBuilder::constantInt(int32_t i)
|
|||
{
|
||||
return constant(Int32Value(i));
|
||||
}
|
||||
|
||||
MDefinition *
|
||||
IonBuilder::getCallee()
|
||||
{
|
||||
if (inliningDepth_ == 0) {
|
||||
MInstruction *callee = MCallee::New(alloc());
|
||||
current->add(callee);
|
||||
return callee;
|
||||
}
|
||||
|
||||
return inlineCallInfo_->fun();
|
||||
}
|
||||
|
|
|
@ -534,6 +534,8 @@ class IonBuilder : public MIRGenerator
|
|||
MInstruction *getTypedArrayLength(MDefinition *obj);
|
||||
MInstruction *getTypedArrayElements(MDefinition *obj);
|
||||
|
||||
MDefinition *getCallee();
|
||||
|
||||
bool jsop_add(MDefinition *left, MDefinition *right);
|
||||
bool jsop_bitnot();
|
||||
bool jsop_bitop(JSOp op);
|
||||
|
|
|
@ -1846,7 +1846,7 @@ MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch
|
|||
// Emit code for the following test:
|
||||
//
|
||||
// bool isInterpretedConstructor() const {
|
||||
// return isInterpreted() && !isFunctionPrototype() &&
|
||||
// return isInterpreted() && !isFunctionPrototype() && !isArrow() &&
|
||||
// (!isSelfHostedBuiltin() || isSelfHostedConstructor());
|
||||
// }
|
||||
|
||||
|
@ -1854,16 +1854,15 @@ MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch
|
|||
load32(Address(fun, JSFunction::offsetOfNargs()), scratch);
|
||||
branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::INTERPRETED << 16), label);
|
||||
|
||||
// Common case: if both IS_FUN_PROTO and SELF_HOSTED are not set,
|
||||
// we're done.
|
||||
// Common case: if IS_FUN_PROTO, ARROW and SELF_HOSTED are not set,
|
||||
// the function is an interpreted constructor and we're done.
|
||||
Label done;
|
||||
uint32_t bits = (JSFunction::IS_FUN_PROTO | JSFunction::SELF_HOSTED) << 16;
|
||||
uint32_t bits = (JSFunction::IS_FUN_PROTO | JSFunction::ARROW | JSFunction::SELF_HOSTED) << 16;
|
||||
branchTest32(Assembler::Zero, scratch, Imm32(bits), &done);
|
||||
{
|
||||
// The callee is either Function.prototype or self-hosted. Fail if
|
||||
// SELF_HOSTED_CTOR is not set. This means the callee must be
|
||||
// Function.prototype or a self-hosted function that's not a
|
||||
// constructor.
|
||||
// The callee is either Function.prototype, an arrow function or
|
||||
// self-hosted. None of these are constructible, except self-hosted
|
||||
// constructors, so branch to |label| if SELF_HOSTED_CTOR is not set.
|
||||
branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::SELF_HOSTED_CTOR << 16), label);
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
|
@ -1056,6 +1056,20 @@ class LComputeThis : public LInstructionHelper<1, BOX_PIECES, 0>
|
|||
}
|
||||
};
|
||||
|
||||
class LLoadArrowThis : public LInstructionHelper<BOX_PIECES, 1, 0>
|
||||
{
|
||||
public:
|
||||
LLoadArrowThis(const LAllocation &callee) {
|
||||
setOperand(0, callee);
|
||||
}
|
||||
|
||||
LIR_HEADER(LoadArrowThis)
|
||||
|
||||
const LAllocation *callee() {
|
||||
return getOperand(0);
|
||||
}
|
||||
};
|
||||
|
||||
// Writes a typed argument for a function call to the frame's argument vector.
|
||||
class LStackArgT : public LInstructionHelper<0, 1, 0>
|
||||
{
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
_(SetArgumentsObjectArg) \
|
||||
_(ReturnFromCtor) \
|
||||
_(ComputeThis) \
|
||||
_(LoadArrowThis) \
|
||||
_(BitNotI) \
|
||||
_(BitNotV) \
|
||||
_(BitOpI) \
|
||||
|
|
|
@ -414,6 +414,16 @@ LIRGenerator::visitComputeThis(MComputeThis *ins)
|
|||
return define(lir, ins) && assignSafepoint(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::visitLoadArrowThis(MLoadArrowThis *ins)
|
||||
{
|
||||
JS_ASSERT(ins->type() == MIRType_Value);
|
||||
JS_ASSERT(ins->callee()->type() == MIRType_Object);
|
||||
|
||||
LLoadArrowThis *lir = new(alloc()) LLoadArrowThis(useRegister(ins->callee()));
|
||||
return defineBox(lir, ins);
|
||||
}
|
||||
|
||||
bool
|
||||
LIRGenerator::lowerCallArguments(MCall *call)
|
||||
{
|
||||
|
|
|
@ -95,6 +95,7 @@ class LIRGenerator : public LIRGeneratorSpecific
|
|||
bool visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins);
|
||||
bool visitReturnFromCtor(MReturnFromCtor *ins);
|
||||
bool visitComputeThis(MComputeThis *ins);
|
||||
bool visitLoadArrowThis(MLoadArrowThis *ins);
|
||||
bool visitCall(MCall *call);
|
||||
bool visitApplyArgs(MApplyArgs *apply);
|
||||
bool visitBail(MBail *bail);
|
||||
|
|
|
@ -1992,6 +1992,9 @@ class MBail : public MNullaryInstruction
|
|||
return new(alloc) MBail();
|
||||
}
|
||||
|
||||
AliasSet getAliasSet() const {
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MAssertFloat32 : public MUnaryInstruction
|
||||
|
@ -4398,6 +4401,39 @@ class MComputeThis
|
|||
// effectful.
|
||||
};
|
||||
|
||||
// Load an arrow function's |this| value.
|
||||
class MLoadArrowThis
|
||||
: public MUnaryInstruction,
|
||||
public SingleObjectPolicy
|
||||
{
|
||||
MLoadArrowThis(MDefinition *callee)
|
||||
: MUnaryInstruction(callee)
|
||||
{
|
||||
setResultType(MIRType_Value);
|
||||
setMovable();
|
||||
}
|
||||
|
||||
public:
|
||||
INSTRUCTION_HEADER(LoadArrowThis)
|
||||
|
||||
static MLoadArrowThis *New(TempAllocator &alloc, MDefinition *callee) {
|
||||
return new(alloc) MLoadArrowThis(callee);
|
||||
}
|
||||
MDefinition *callee() const {
|
||||
return getOperand(0);
|
||||
}
|
||||
TypePolicy *typePolicy() {
|
||||
return this;
|
||||
}
|
||||
bool congruentTo(MDefinition *ins) const {
|
||||
return congruentIfOperandsEqual(ins);
|
||||
}
|
||||
AliasSet getAliasSet() const {
|
||||
// An arrow function's lexical |this| value is immutable.
|
||||
return AliasSet::None();
|
||||
}
|
||||
};
|
||||
|
||||
class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi>
|
||||
{
|
||||
js::Vector<MUse, 2, IonAllocPolicy> inputs_;
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace jit {
|
|||
_(GetArgumentsObjectArg) \
|
||||
_(SetArgumentsObjectArg) \
|
||||
_(ComputeThis) \
|
||||
_(LoadArrowThis) \
|
||||
_(Call) \
|
||||
_(ApplyArgs) \
|
||||
_(Bail) \
|
||||
|
|
|
@ -136,6 +136,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor
|
|||
UNSAFE_OP(GetArgumentsObjectArg)
|
||||
UNSAFE_OP(SetArgumentsObjectArg)
|
||||
UNSAFE_OP(ComputeThis)
|
||||
UNSAFE_OP(LoadArrowThis)
|
||||
CUSTOM_OP(Call)
|
||||
UNSAFE_OP(ApplyArgs)
|
||||
UNSAFE_OP(Bail)
|
||||
|
|
|
@ -165,7 +165,7 @@ fun_enumerate(JSContext *cx, HandleObject obj)
|
|||
RootedId id(cx);
|
||||
bool found;
|
||||
|
||||
if (!obj->isBoundFunction()) {
|
||||
if (!obj->isBoundFunction() && !obj->as<JSFunction>().isArrow()) {
|
||||
id = NameToId(cx->names().prototype);
|
||||
if (!JSObject::hasProperty(cx, obj, id, &found, 0))
|
||||
return false;
|
||||
|
@ -286,8 +286,10 @@ js::fun_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
|
|||
* ES5 15.3.4.5: bound functions don't have a prototype property. The
|
||||
* isBuiltin() test covers this case because bound functions are native
|
||||
* (and thus built-in) functions by definition/construction.
|
||||
*
|
||||
* ES6 19.2.4.3: arrow functions also don't have a prototype property.
|
||||
*/
|
||||
if (fun->isBuiltin() || fun->isFunctionPrototype())
|
||||
if (fun->isBuiltin() || fun->isArrow() || fun->isFunctionPrototype())
|
||||
return true;
|
||||
|
||||
if (!ResolveInterpretedFunctionPrototype(cx, fun))
|
||||
|
@ -420,6 +422,11 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
|
|||
if (!xdr->codeUint32(&firstword))
|
||||
return false;
|
||||
|
||||
if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&flagsword))
|
||||
return false;
|
||||
|
||||
if (mode == XDR_DECODE) {
|
||||
JSObject *proto = nullptr;
|
||||
if (firstword & IsStarGenerator) {
|
||||
|
@ -428,20 +435,17 @@ js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, Han
|
|||
return false;
|
||||
}
|
||||
|
||||
gc::AllocKind allocKind = uint16_t(flagsword) & JSFunction::EXTENDED
|
||||
? JSFunction::ExtendedFinalizeKind
|
||||
: JSFunction::FinalizeKind;
|
||||
fun = NewFunctionWithProto(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED,
|
||||
/* parent = */ NullPtr(), NullPtr(), proto,
|
||||
JSFunction::FinalizeKind, TenuredObject);
|
||||
allocKind, TenuredObject);
|
||||
if (!fun)
|
||||
return false;
|
||||
atom = nullptr;
|
||||
script = nullptr;
|
||||
}
|
||||
|
||||
if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
|
||||
return false;
|
||||
if (!xdr->codeUint32(&flagsword))
|
||||
return false;
|
||||
|
||||
if (firstword & IsLazy) {
|
||||
if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy))
|
||||
return false;
|
||||
|
@ -487,10 +491,13 @@ js::CloneFunctionAndScript(JSContext *cx, HandleObject enclosingScope, HandleFun
|
|||
if (!cloneProto)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gc::AllocKind allocKind = srcFun->isExtended()
|
||||
? JSFunction::ExtendedFinalizeKind
|
||||
: JSFunction::FinalizeKind;
|
||||
RootedFunction clone(cx, NewFunctionWithProto(cx, NullPtr(), nullptr, 0,
|
||||
JSFunction::INTERPRETED, NullPtr(), NullPtr(),
|
||||
cloneProto, JSFunction::FinalizeKind,
|
||||
TenuredObject));
|
||||
cloneProto, allocKind, TenuredObject));
|
||||
if (!clone)
|
||||
return nullptr;
|
||||
|
||||
|
@ -682,15 +689,6 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb
|
|||
if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx))
|
||||
return nullptr;
|
||||
|
||||
// If the object is an automatically-bound arrow function, get the source
|
||||
// of the pre-binding target.
|
||||
if (fun->isArrow() && fun->isBoundFunction()) {
|
||||
JSObject *target = fun->getBoundFunctionTarget();
|
||||
RootedFunction targetFun(cx, &target->as<JSFunction>());
|
||||
JS_ASSERT(targetFun->isArrow());
|
||||
return FunctionToString(cx, targetFun, bodyOnly, lambdaParen);
|
||||
}
|
||||
|
||||
if (IsAsmJSModule(fun))
|
||||
return AsmJSModuleToString(cx, fun, !lambdaParen);
|
||||
if (IsAsmJSFunction(fun))
|
||||
|
@ -1248,17 +1246,6 @@ js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
|
|||
RootedFunction fun(cx, &args.callee().as<JSFunction>());
|
||||
JS_ASSERT(fun->isBoundFunction());
|
||||
|
||||
bool constructing = args.isConstructing();
|
||||
if (constructing && fun->isArrow()) {
|
||||
/*
|
||||
* Per spec, arrow functions do not even have a [[Construct]] method.
|
||||
* So before anything else, if we are an arrow function, make sure we
|
||||
* don't even get here. You never saw me. Burn this comment.
|
||||
*/
|
||||
RootedValue v(cx, ObjectValue(*fun));
|
||||
return ReportIsNotFunction(cx, v, -1, CONSTRUCT);
|
||||
}
|
||||
|
||||
/* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
|
||||
unsigned argslen = fun->getBoundFunctionArgumentCount();
|
||||
|
||||
|
@ -1285,6 +1272,7 @@ js::CallOrConstructBoundFunction(JSContext *cx, unsigned argc, Value *vp)
|
|||
/* 15.3.4.5.1, 15.3.4.5.2 step 5. */
|
||||
invokeArgs.setCallee(ObjectValue(*target));
|
||||
|
||||
bool constructing = args.isConstructing();
|
||||
if (!constructing)
|
||||
invokeArgs.setThis(boundThis);
|
||||
|
||||
|
|
|
@ -142,17 +142,7 @@ class JSFunction : public JSObject
|
|||
return nonLazyScript()->hasBaselineScript() || nonLazyScript()->hasIonScript();
|
||||
}
|
||||
|
||||
// Arrow functions are a little weird.
|
||||
//
|
||||
// Like all functions, (1) when the compiler parses an arrow function, it
|
||||
// creates a function object that gets stored with the enclosing script;
|
||||
// and (2) at run time the script's function object is cloned.
|
||||
//
|
||||
// But then, unlike other functions, (3) a bound function is created with
|
||||
// the clone as its target.
|
||||
//
|
||||
// isArrow() is true for all three of these Function objects.
|
||||
// isBoundFunction() is true only for the last one.
|
||||
// Arrow functions store their lexical |this| in the first extended slot.
|
||||
bool isArrow() const { return flags() & ARROW; }
|
||||
|
||||
/* Compound attributes: */
|
||||
|
@ -162,7 +152,7 @@ class JSFunction : public JSObject
|
|||
bool isInterpretedConstructor() const {
|
||||
// Note: the JITs inline this check, so be careful when making changes
|
||||
// here. See IonMacroAssembler::branchIfNotInterpretedConstructor.
|
||||
return isInterpreted() && !isFunctionPrototype() &&
|
||||
return isInterpreted() && !isFunctionPrototype() && !isArrow() &&
|
||||
(!isSelfHostedBuiltin() || isSelfHostedConstructor());
|
||||
}
|
||||
bool isNamedLambda() const {
|
||||
|
@ -548,6 +538,10 @@ class FunctionExtended : public JSFunction
|
|||
public:
|
||||
static const unsigned NUM_EXTENDED_SLOTS = 2;
|
||||
|
||||
static inline size_t offsetOfArrowThisSlot() {
|
||||
return offsetof(FunctionExtended, extendedSlots) + 0 * sizeof(HeapValue);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class JSFunction;
|
||||
|
||||
|
|
|
@ -2730,62 +2730,54 @@ UpdatePropertyType(ExclusiveContext *cx, HeapTypeSet *types, JSObject *obj, Shap
|
|||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TypeObject::addProperty(ExclusiveContext *cx, jsid id, Property **pprop)
|
||||
void
|
||||
TypeObject::updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types)
|
||||
{
|
||||
JS_ASSERT(!*pprop);
|
||||
Property *base = cx->typeLifoAlloc().new_<Property>(id);
|
||||
if (!base)
|
||||
return false;
|
||||
|
||||
if (singleton() && singleton()->isNative()) {
|
||||
/*
|
||||
* Fill the property in with any type the object already has in an own
|
||||
* property. We are only interested in plain native properties and
|
||||
* dense elements which don't go through a barrier when read by the VM
|
||||
* or jitcode.
|
||||
*/
|
||||
|
||||
if (JSID_IS_VOID(id)) {
|
||||
/* Go through all shapes on the object to get integer-valued properties. */
|
||||
RootedShape shape(cx, singleton()->lastProperty());
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (JSID_IS_VOID(IdToTypeId(shape->propid())))
|
||||
UpdatePropertyType(cx, &base->types, singleton(), shape, true);
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
/* Also get values of any dense elements in the object. */
|
||||
for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) {
|
||||
const Value &value = singleton()->getDenseElement(i);
|
||||
if (!value.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
Type type = GetValueType(value);
|
||||
base->types.TypeSet::addType(type, &cx->typeLifoAlloc());
|
||||
}
|
||||
}
|
||||
} else if (!JSID_IS_EMPTY(id)) {
|
||||
RootedId rootedId(cx, id);
|
||||
Shape *shape = singleton()->nativeLookup(cx, rootedId);
|
||||
if (shape)
|
||||
UpdatePropertyType(cx, &base->types, singleton(), shape, false);
|
||||
}
|
||||
|
||||
if (singleton()->watched()) {
|
||||
/*
|
||||
* Mark the property as non-data, to inhibit optimizations on it
|
||||
* and avoid bypassing the watchpoint handler.
|
||||
*/
|
||||
base->types.setNonDataProperty(cx);
|
||||
}
|
||||
}
|
||||
|
||||
*pprop = base;
|
||||
|
||||
InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s",
|
||||
InferSpewColor(&base->types), &base->types, InferSpewColorReset(),
|
||||
InferSpewColor(types), types, InferSpewColorReset(),
|
||||
TypeObjectString(this), TypeIdString(id));
|
||||
|
||||
return true;
|
||||
if (!singleton() || !singleton()->isNative())
|
||||
return;
|
||||
|
||||
/*
|
||||
* Fill the property in with any type the object already has in an own
|
||||
* property. We are only interested in plain native properties and
|
||||
* dense elements which don't go through a barrier when read by the VM
|
||||
* or jitcode.
|
||||
*/
|
||||
|
||||
if (JSID_IS_VOID(id)) {
|
||||
/* Go through all shapes on the object to get integer-valued properties. */
|
||||
RootedShape shape(cx, singleton()->lastProperty());
|
||||
while (!shape->isEmptyShape()) {
|
||||
if (JSID_IS_VOID(IdToTypeId(shape->propid())))
|
||||
UpdatePropertyType(cx, types, singleton(), shape, true);
|
||||
shape = shape->previous();
|
||||
}
|
||||
|
||||
/* Also get values of any dense elements in the object. */
|
||||
for (size_t i = 0; i < singleton()->getDenseInitializedLength(); i++) {
|
||||
const Value &value = singleton()->getDenseElement(i);
|
||||
if (!value.isMagic(JS_ELEMENTS_HOLE)) {
|
||||
Type type = GetValueType(value);
|
||||
types->TypeSet::addType(type, &cx->typeLifoAlloc());
|
||||
}
|
||||
}
|
||||
} else if (!JSID_IS_EMPTY(id)) {
|
||||
RootedId rootedId(cx, id);
|
||||
Shape *shape = singleton()->nativeLookup(cx, rootedId);
|
||||
if (shape)
|
||||
UpdatePropertyType(cx, types, singleton(), shape, false);
|
||||
}
|
||||
|
||||
if (singleton()->watched()) {
|
||||
/*
|
||||
* Mark the property as non-data, to inhibit optimizations on it
|
||||
* and avoid bypassing the watchpoint handler.
|
||||
*/
|
||||
types->setNonDataProperty(cx);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1108,7 +1108,7 @@ struct TypeObject : gc::BarrieredCell<TypeObject>
|
|||
|
||||
/* Helpers */
|
||||
|
||||
bool addProperty(ExclusiveContext *cx, jsid id, Property **pprop);
|
||||
void updateNewPropertyTypes(ExclusiveContext *cx, jsid id, HeapTypeSet *types);
|
||||
bool addDefiniteProperties(ExclusiveContext *cx, JSObject *obj);
|
||||
bool matchDefiniteProperties(HandleObject obj);
|
||||
void addPrototype(JSContext *cx, TypeObject *proto);
|
||||
|
|
|
@ -1238,6 +1238,12 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id)
|
|||
if (HeapTypeSet *types = maybeGetProperty(id))
|
||||
return types;
|
||||
|
||||
Property *base = cx->typeLifoAlloc().new_<Property>(id);
|
||||
if (!base) {
|
||||
markUnknown(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t propertyCount = basePropertyCount();
|
||||
Property **pprop = HashSetInsert<jsid,Property,Property>
|
||||
(cx->typeLifoAlloc(), propertySet, propertyCount, id);
|
||||
|
@ -1249,28 +1255,18 @@ TypeObject::getProperty(ExclusiveContext *cx, jsid id)
|
|||
JS_ASSERT(!*pprop);
|
||||
|
||||
setBasePropertyCount(propertyCount);
|
||||
if (!addProperty(cx, id, pprop)) {
|
||||
markUnknown(cx);
|
||||
return nullptr;
|
||||
}
|
||||
*pprop = base;
|
||||
|
||||
updateNewPropertyTypes(cx, id, &base->types);
|
||||
|
||||
if (propertyCount == OBJECT_FLAG_PROPERTY_COUNT_LIMIT) {
|
||||
// We hit the maximum number of properties the object can have, mark
|
||||
// the object unknown so that new properties will not be added in the
|
||||
// future.
|
||||
markUnknown(cx);
|
||||
|
||||
/*
|
||||
* Return an arbitrary property in the object, as all have unknown
|
||||
* type and are treated as non-data properties.
|
||||
*/
|
||||
unsigned count = getPropertyCount();
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (Property *prop = getProperty(i))
|
||||
return &prop->types;
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("Missing property");
|
||||
}
|
||||
|
||||
return &(*pprop)->types;
|
||||
return &base->types;
|
||||
}
|
||||
|
||||
inline HeapTypeSet *
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "jsinferinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
||||
#include "vm/Stack-inl.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
|
|
@ -31,6 +31,16 @@ inline bool
|
|||
ComputeThis(JSContext *cx, AbstractFramePtr frame)
|
||||
{
|
||||
JS_ASSERT_IF(frame.isInterpreterFrame(), !frame.asInterpreterFrame()->runningInJit());
|
||||
|
||||
if (frame.isFunctionFrame() && frame.fun()->isArrow()) {
|
||||
/*
|
||||
* Arrow functions store their (lexical) |this| value in an
|
||||
* extended slot.
|
||||
*/
|
||||
frame.thisValue() = frame.fun()->getExtendedSlot(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (frame.thisValue().isObject())
|
||||
return true;
|
||||
RootedValue thisv(cx, frame.thisValue());
|
||||
|
|
|
@ -3627,10 +3627,8 @@ js::LambdaArrow(JSContext *cx, HandleFunction fun, HandleObject parent, HandleVa
|
|||
if (!clone)
|
||||
return nullptr;
|
||||
|
||||
clone = js_fun_bind(cx, clone, thisv, nullptr, 0);
|
||||
if (!clone)
|
||||
return nullptr;
|
||||
clone->as<JSFunction>().setArrow();
|
||||
MOZ_ASSERT(clone->as<JSFunction>().isArrow());
|
||||
clone->as<JSFunction>().setExtendedSlot(0, thisv);
|
||||
|
||||
MOZ_ASSERT(clone->global() == clone->global());
|
||||
return clone;
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace js {
|
|||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 170);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 171);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include "nsIScrollPositionListener.h"
|
||||
#include "StickyScrollContainer.h"
|
||||
#include "nsIFrameInlines.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include <algorithm>
|
||||
#include <cstdlib> // for std::abs(int/long)
|
||||
#include <cmath> // for std::abs(float/double)
|
||||
|
@ -2585,10 +2586,10 @@ ScrollFrameHelper::BuildDisplayList(nsDisplayListBuilder* aBuilder,
|
|||
bool wantLayerV = isVScrollable && (mVScrollbarBox || isFocused);
|
||||
bool wantLayerH = isHScrollable && (mHScrollbarBox || isFocused);
|
||||
// TODO Turn this on for inprocess OMTC on all platforms
|
||||
bool wantSubAPZC = (XRE_GetProcessType() == GeckoProcessType_Content);
|
||||
#ifdef XP_WIN
|
||||
if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
|
||||
wantSubAPZC = true;
|
||||
bool wantSubAPZC = gfxPrefs::APZSubframeEnabled();
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Content) {
|
||||
wantSubAPZC = false;
|
||||
}
|
||||
#endif
|
||||
shouldBuildLayer =
|
||||
|
|
|
@ -60,3 +60,123 @@ function is_approx(float1, float2, error, desc) {
|
|||
ok(Math.abs(float1 - float2) < error,
|
||||
desc + ": " + float1 + " and " + float2 + " should be within " + error);
|
||||
}
|
||||
|
||||
// Checks if off-main thread animation (OMTA) is available, and if it is, runs
|
||||
// the provided callback function. If OMTA is not available or is not
|
||||
// functioning correctly, the second callback, aOnSkip, is run instead.
|
||||
//
|
||||
// This function also does an internal test to verify that OMTA is working at
|
||||
// all so that if OMTA is not functioning correctly when it is expected to
|
||||
// function only a single failure is produced.
|
||||
//
|
||||
// Since this function relies on various asynchronous operations, the caller is
|
||||
// responsible for calling SimpleTest.waitForExplicitFinish() before calling
|
||||
// this and SimpleTest.finish() within aTestFunction and aOnSkip.
|
||||
function runOMTATest(aTestFunction, aOnSkip) {
|
||||
const OMTAPrefKey = "layers.offmainthreadcomposition.async-animations";
|
||||
var utils = SpecialPowers.DOMWindowUtils;
|
||||
var expectOMTA = utils.layerManagerRemote &&
|
||||
// ^ Off-main thread animation cannot be used if off-main
|
||||
// thread composition (OMTC) is not available
|
||||
SpecialPowers.getBoolPref(OMTAPrefKey);
|
||||
|
||||
isOMTAWorking().then(function(isWorking) {
|
||||
if (expectOMTA) {
|
||||
if (isWorking) {
|
||||
aTestFunction();
|
||||
} else {
|
||||
// We only call this when we know it will fail as otherwise in the
|
||||
// regular success case we will end up inflating the "passed tests"
|
||||
// count by 1
|
||||
ok(isWorking, "OMTA is working as expected");
|
||||
aOnSkip();
|
||||
}
|
||||
} else {
|
||||
todo(isWorking, "OMTA is working");
|
||||
aOnSkip();
|
||||
}
|
||||
}).catch(function(err) {
|
||||
ok(false, err);
|
||||
aOnSkip();
|
||||
});
|
||||
|
||||
function isOMTAWorking() {
|
||||
// Create keyframes rule
|
||||
const animationName = "a6ce3091ed85"; // Random name to avoid clashes
|
||||
var ruleText = "@keyframes " + animationName +
|
||||
" { from { opacity: 0.5 } to { opacity 0.5 } }";
|
||||
var style = document.createElement("style");
|
||||
style.appendChild(document.createTextNode(ruleText));
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Create animation target
|
||||
var div = document.createElement("div");
|
||||
document.body.appendChild(div);
|
||||
|
||||
// Give the target geometry so it is eligible for layerization
|
||||
div.style.width = "100px";
|
||||
div.style.height = "100px";
|
||||
div.style.backgroundColor = "white";
|
||||
|
||||
// Common clean up code
|
||||
var cleanUp = function() {
|
||||
div.parentNode.removeChild(div);
|
||||
style.parentNode.removeChild(style);
|
||||
if (utils.isTestControllingRefreshes) {
|
||||
utils.restoreNormalRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
return waitForDocumentLoad()
|
||||
.then(loadPaintListener)
|
||||
.then(function() {
|
||||
// Put refresh driver under test control and trigger animation
|
||||
utils.advanceTimeAndRefresh(0);
|
||||
div.style.animation = animationName + " 10s";
|
||||
|
||||
// Trigger style flush
|
||||
div.clientTop;
|
||||
return waitForPaints();
|
||||
}).then(function() {
|
||||
var opacity = utils.getOMTAStyle(div, "opacity");
|
||||
cleanUp();
|
||||
return Promise.resolve(opacity == 0.5);
|
||||
}).catch(function(err) {
|
||||
cleanUp();
|
||||
return Promise.reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
function waitForDocumentLoad() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (document.readyState === "complete") {
|
||||
resolve();
|
||||
} else {
|
||||
window.addEventListener("load", resolve);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function waitForPaints() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitForAllPaintsFlushed(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function loadPaintListener() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof(window.waitForAllPaints) !== "function") {
|
||||
var script = document.createElement("script");
|
||||
script.onload = resolve;
|
||||
script.onerror = function() {
|
||||
reject(new Error("Failed to load paint listener"));
|
||||
};
|
||||
script.src = "/tests/SimpleTest/paint_listener.js";
|
||||
var firstScript = document.scripts[0];
|
||||
firstScript.parentNode.insertBefore(script, firstScript);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ generated-files = css_properties.js
|
|||
[test_all_shorthand.html]
|
||||
[test_animations.html]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_animations_omta.html]
|
||||
[test_animations_omta_start.html]
|
||||
[test_any_dynamic.html]
|
||||
[test_at_rule_parse_serialize.html]
|
||||
|
|
|
@ -2,6 +2,17 @@
|
|||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=435442
|
||||
-->
|
||||
<!--
|
||||
|
||||
====== PLEASE KEEP THIS IN SYNC WITH test_animations_omta.html =======
|
||||
|
||||
test_animations_omta.html mimicks the content of this file but with
|
||||
extra machinery for testing animation values on the compositor thread.
|
||||
|
||||
If you are making changes to this file or to test_animations_omta.html, please
|
||||
try to keep them consistent where appropriate.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<title>Test for css3-animations (Bug 435442)</title>
|
||||
|
|
|
@ -0,0 +1,954 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=964646
|
||||
-->
|
||||
<!--
|
||||
|
||||
========= PLEASE KEEP THIS IN SYNC WITH test_animations.html =========
|
||||
|
||||
This test mimicks the content of test_animations.html but performs tests
|
||||
specific to animations that run on the compositor thread since they require
|
||||
special (asynchronous) handling. Furthermore, these tests check that
|
||||
animations that are expected to run on the compositor thread, are actually
|
||||
doing so.
|
||||
|
||||
If you are making changes to this file or to test_animations.html, please
|
||||
try to keep them consistent where appropriate.
|
||||
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for css3-animations running on the compositor thread (Bug
|
||||
964646)</title>
|
||||
<script type="application/javascript"
|
||||
src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="animation_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
@keyframes transform-anim {
|
||||
to {
|
||||
transform: translate(100px);
|
||||
}
|
||||
}
|
||||
@keyframes anim1 {
|
||||
0% { transform: translate(0px) }
|
||||
50% { transform: translate(80px) }
|
||||
100% { transform: translate(100px) }
|
||||
}
|
||||
@keyframes anim2 {
|
||||
from { opacity: 0 } to { opacity: 1 }
|
||||
}
|
||||
@keyframes anim3 {
|
||||
from { opacity: 0 } to { opacity: 1 }
|
||||
}
|
||||
|
||||
@keyframes kf1 {
|
||||
50% { transform: translate(50px) }
|
||||
to { transform: translate(150px) }
|
||||
}
|
||||
@keyframes kf2 {
|
||||
from { transform: translate(150px) }
|
||||
50% { transform: translate(50px) }
|
||||
}
|
||||
@keyframes kf3 {
|
||||
25% { transform: translate(100px) }
|
||||
}
|
||||
@keyframes kf4 {
|
||||
to, from { display: inline; transform: translate(37px) }
|
||||
}
|
||||
@keyframes kf_cascade1 {
|
||||
from { transform: translate(50px) }
|
||||
50%, from { transform: translate(30px) } /* wins: 0% */
|
||||
75%, 85%, 50% { transform: translate(20px) } /* wins: 75%, 50% */
|
||||
100%, 85% { transform: translate(70px) } /* wins: 100% */
|
||||
85.1% { transform: translate(60px) } /* wins: 85.1% */
|
||||
85% { transform: translate(30px) } /* wins: 85% */
|
||||
}
|
||||
@keyframes kf_cascade2 { from, to { opacity: 0.3 } }
|
||||
@keyframes kf_cascade2 { from, to { transform: translate(50px) } }
|
||||
@keyframes kf_cascade2 { from, to { transform: translate(100px) } }
|
||||
|
||||
.target {
|
||||
/* The animation target needs geometry in order to qualify for OMTA */
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background-color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=964646">Mozilla Bug
|
||||
964646</a>
|
||||
<div id="display"></div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
/** Test for css3-animations running on the compositor thread (Bug 964646) **/
|
||||
|
||||
// Global state
|
||||
var gAsyncTests = [],
|
||||
gDisplay = document.getElementById("display"),
|
||||
gDiv = null,
|
||||
gEventsReceived = [];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runOMTATest(function() {
|
||||
// The async test runner returns a Promise that is resolved when the
|
||||
// test is finished so we can chain them together
|
||||
gAsyncTests.reduce(function(sequence, test) {
|
||||
return sequence.then(function() { return runAsyncTest(test); });
|
||||
}, Promise.resolve() /* the start of the sequence */)
|
||||
// Final step in the sequence
|
||||
.then(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}, SimpleTest.finish);
|
||||
|
||||
// Takes a generator function that represents a test case. Each point in the
|
||||
// test case that waits asynchronously for some result yields a Promise that is
|
||||
// resolved when the asychronous action has completed. By chaining these
|
||||
// intermediate results together we run the test to completion.
|
||||
//
|
||||
// This method itself returns a Promise that is resolved when the generator
|
||||
// function has completed.
|
||||
//
|
||||
// This arrangement is based on add_task() which is currently only available
|
||||
// in mochitest-chrome (bug 872229). Once add_task is available in
|
||||
// mochitest-plain we can remove this function and use add_task instead.
|
||||
function runAsyncTest(test) {
|
||||
var generator;
|
||||
|
||||
function step(arg) {
|
||||
var next;
|
||||
try {
|
||||
next = generator.next(arg);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
if (next.done) {
|
||||
return Promise.resolve(next.value);
|
||||
} else {
|
||||
return Promise.resolve(next.value)
|
||||
.then(step, function(err) { throw err; });
|
||||
}
|
||||
}
|
||||
|
||||
// Put refresh driver under test control
|
||||
advance_clock(0);
|
||||
|
||||
// Run test
|
||||
generator = test();
|
||||
return step()
|
||||
.catch(function(err) {
|
||||
ok(false, err.message);
|
||||
// Clear up the test div in case we aborted the test before doing clean-up
|
||||
if (gDiv) {
|
||||
done_div();
|
||||
}
|
||||
}).then(function() {
|
||||
// Restore clock
|
||||
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
|
||||
});
|
||||
}
|
||||
|
||||
function addAsyncTest(generator) {
|
||||
gAsyncTests.push(generator);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// Test cases
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// This test is not in test_animations.html but is here to test that
|
||||
// transform animations are actually run on the compositor thread as expected.
|
||||
addAsyncTest(function *() {
|
||||
new_div("animation: transform-anim linear 300s");
|
||||
|
||||
yield waitForPaints();
|
||||
|
||||
advance_clock(200000);
|
||||
omta_is("transform", { tx: 100 * 2 / 3 }, RunningOn.Compositor,
|
||||
"OMTA animation is animating as expected");
|
||||
done_div();
|
||||
});
|
||||
|
||||
function *testFillMode(fillMode, fillsBackwards, fillsForwards)
|
||||
{
|
||||
var style = "transform: translate(30px); animation: 10s 3s anim1 linear";
|
||||
var desc;
|
||||
if (fillMode.length > 0) {
|
||||
style += " " + fillMode;
|
||||
desc = "fill mode " + fillMode + ": ";
|
||||
} else {
|
||||
desc = "default fill mode: ";
|
||||
}
|
||||
new_div(style);
|
||||
listen();
|
||||
|
||||
// Currently backwards fill is not performed on the compositor thread but we
|
||||
// should wait for paints so we can test that transform values are *not* being
|
||||
// set on the compositor thread.
|
||||
yield waitForPaints();
|
||||
|
||||
if (fillsBackwards)
|
||||
omta_is("transform", { tx: 0 }, RunningOn.MainThread,
|
||||
desc + "does affect value during delay (0s)");
|
||||
else
|
||||
omta_is("transform", { tx: 30 }, RunningOn.MainThread,
|
||||
desc + "doesn't affect value during delay (0s)");
|
||||
|
||||
advance_clock(2000);
|
||||
if (fillsBackwards)
|
||||
omta_is("transform", { tx: 0 }, RunningOn.MainThead,
|
||||
desc + "does affect value during delay (0s)");
|
||||
else
|
||||
omta_is("transform", { tx: 30 }, RunningOn.MainThread,
|
||||
desc + "does affect value during delay (0s)");
|
||||
|
||||
check_events([], "before start in testFillMode");
|
||||
advance_clock(1000);
|
||||
check_events([{ type: "animationstart", target: gDiv,
|
||||
bubbles: true, cancelable: false,
|
||||
animationName: "anim1", elapsedTime: 0.0,
|
||||
pseudoElement: "" }],
|
||||
"right after start in testFillMode");
|
||||
|
||||
// If we have a backwards fill then at the start of the animation we will end
|
||||
// up applying the same value as the fill value. Various optimizations in
|
||||
// RestyleManager may filter out this meaning that the animation doesn't get
|
||||
// added to the compositor thread until the first time the value changes.
|
||||
//
|
||||
// As a result we look for this first sample on either the compositor or the
|
||||
// computed style
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 0 }, RunningOn.Either,
|
||||
desc + "affects value at start of animation");
|
||||
advance_clock(125);
|
||||
// We might not add the animation to compositor until the second sample (due
|
||||
// to the optimizations mentioned above) so we should wait for paints before
|
||||
// proceeding
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 2 }, RunningOn.Compositor,
|
||||
desc + "affects value during animation");
|
||||
advance_clock(2375);
|
||||
omta_is("transform", { tx: 40 }, RunningOn.Compositor,
|
||||
desc + "affects value during animation");
|
||||
advance_clock(2500);
|
||||
omta_is("transform", { tx: 80 }, RunningOn.Compositor,
|
||||
desc + "affects value during animation");
|
||||
advance_clock(2500);
|
||||
omta_is("transform", { tx: 90 }, RunningOn.Compositor,
|
||||
desc + "affects value during animation");
|
||||
advance_clock(2375);
|
||||
omta_is("transform", { tx: 99.5 }, RunningOn.Compositor,
|
||||
desc + "affects value during animation");
|
||||
check_events([], "before end in testFillMode");
|
||||
advance_clock(125);
|
||||
check_events([{ type: "animationend", target: gDiv,
|
||||
bubbles: true, cancelable: false,
|
||||
animationName: "anim1", elapsedTime: 10.0,
|
||||
pseudoElement: "" }],
|
||||
"right after end in testFillMode");
|
||||
|
||||
// Currently the compositor will apply a forwards fill until it gets told by
|
||||
// the main thread to clear the animation. As a result we should wait for
|
||||
// paints to be flushed before checking that the animated value does *not*
|
||||
// appear on the compositor thread.
|
||||
yield waitForPaints();
|
||||
if (fillsForwards)
|
||||
omta_is("transform", { tx: 100 }, RunningOn.MainThread,
|
||||
desc + "affects value at end of animation");
|
||||
advance_clock(10);
|
||||
if (fillsForwards)
|
||||
omta_is("transform", { tx: 100 }, RunningOn.MainThread,
|
||||
desc + "affects value after animation");
|
||||
else
|
||||
omta_is("transform", { tx: 30 }, RunningOn.MainThread,
|
||||
desc + "does not affect value after animation");
|
||||
|
||||
done_div();
|
||||
}
|
||||
|
||||
addAsyncTest(function() { return testFillMode("", false, false); });
|
||||
addAsyncTest(function() { return testFillMode("none", false, false); });
|
||||
addAsyncTest(function() { return testFillMode("forwards", false, true); });
|
||||
addAsyncTest(function() { return testFillMode("backwards", true, false); });
|
||||
addAsyncTest(function() { return testFillMode("both", true, true); });
|
||||
|
||||
// Test that animations continue running when the animation name
|
||||
// list is changed.
|
||||
//
|
||||
// test_animations.html combines all these tests into one block but this is
|
||||
// difficult for OMTA because currently there are only two properties to which
|
||||
// we apply OMTA. Instead we break the test down into a few independent pieces
|
||||
// in order to exercise the same functionality.
|
||||
|
||||
// Append to list
|
||||
addAsyncTest(function *() {
|
||||
new_div("animation: anim1 linear 10s");
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 0 }, RunningOn.Either,
|
||||
"just anim1, translate at start");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"just anim1, translate at 1s");
|
||||
// append anim2
|
||||
gDiv.style.animation = "anim1 linear 10s, anim2 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"anim1 + anim2, translate at 1s");
|
||||
omta_is("opacity", 0, RunningOn.Compositor,
|
||||
"anim1 + anim2, opacity at 1s");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"anim1 + anim2, translate at 2s");
|
||||
omta_is("opacity", 0.1, RunningOn.Compositor,
|
||||
"anim1 + anim2, opacity at 2s");
|
||||
done_div();
|
||||
});
|
||||
|
||||
// Prepend to list; delete from list
|
||||
addAsyncTest(function *() {
|
||||
new_div("animation: anim1 linear 10s");
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 0 }, RunningOn.Either,
|
||||
"just anim1, translate at start");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"just anim1, translate at 1s");
|
||||
// prepend anim2
|
||||
gDiv.style.animation = "anim2 linear 10s, anim1 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"anim2 + anim1, translate at 1s");
|
||||
omta_is("opacity", 0, RunningOn.Compositor,
|
||||
"anim2 + anim1, opacity at 1s");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"anim2 + anim1, translate at 2s");
|
||||
omta_is("opacity", 0.1, RunningOn.Compositor,
|
||||
"anim2 + anim1, opacity at 2s");
|
||||
// remove anim2 from list
|
||||
gDiv.style.animation = "anim1 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"just anim1, translate at 2s");
|
||||
omta_is("opacity", 1, RunningOn.MainThread, "just anim1, opacity at 2s");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 48 }, RunningOn.Compositor,
|
||||
"just anim1, translate at 3s");
|
||||
omta_is("opacity", 1, RunningOn.MainThread, "just anim1, opacity at 3s");
|
||||
done_div();
|
||||
});
|
||||
|
||||
// Swap elements
|
||||
addAsyncTest(function *() {
|
||||
new_div("animation: anim1 linear 10s, anim2 linear 10s");
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 0 }, RunningOn.Either,
|
||||
"anim1 + anim2, translate at start");
|
||||
omta_is("opacity", 0, RunningOn.Compositor,
|
||||
"anim1 + anim2, opacity at start");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"anim1 + anim2, translate at 1s");
|
||||
omta_is("opacity", 0.1, RunningOn.Compositor,
|
||||
"anim1 + anim2, opacity at 1s");
|
||||
// swap anim1 and anim2, change duration of anim2
|
||||
gDiv.style.animation = "anim2 linear 5s, anim1 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 16 }, RunningOn.Compositor,
|
||||
"anim2 + anim1, translate at 1s");
|
||||
omta_is("opacity", 0.2, RunningOn.Compositor,
|
||||
"anim2 + anim1, opacity at 1s");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"anim2 + anim1, translate at 2s");
|
||||
omta_is("opacity", 0.4, RunningOn.Compositor,
|
||||
"anim2 + anim1, opacity at 2s");
|
||||
// list anim2 twice, last duration wins, original start time still applies
|
||||
gDiv.style.animation = "anim2 linear 5s, anim1 linear 10s, anim2 linear 20s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"anim2 + anim1 + anim2, translate at 2s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
|
||||
"anim2 + anim1 + anim2, opacity at 2s");
|
||||
// drop one of the anim2, and list anim3 as well, which animates
|
||||
// the same property as anim2
|
||||
gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 32 }, RunningOn.Compositor,
|
||||
"anim1 + anim2 + anim3, translate at 2s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0",
|
||||
"anim1 + anim2 + anim3, opacity at 2s");
|
||||
advance_clock(1000);
|
||||
omta_is("transform", { tx: 48 }, RunningOn.Compositor,
|
||||
"anim1 + anim2 + anim3, translate at 3s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
|
||||
"anim1 + anim2 + anim3, opacity at 3s");
|
||||
// now swap the anim3 and anim2 order
|
||||
gDiv.style.animation = "anim1 linear 10s, anim3 linear 10s, anim2 linear 20s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 48 }, RunningOn.Compositor,
|
||||
"anim1 + anim3 + anim2, translate at 3s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.15",
|
||||
"anim1 + anim3 + anim2, opacity at 3s");
|
||||
advance_clock(2000); // (unlike test_animations.html, we seek 2s forwards here
|
||||
// since at 4s anim2 and anim3 produce the same result so
|
||||
// we can't tell which won.)
|
||||
omta_is("transform", { tx: 80 }, RunningOn.Compositor,
|
||||
"anim1 + anim3 + anim2, translate at 5s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.25",
|
||||
"anim1 + anim3 + anim2, opacity at 5s");
|
||||
// swap anim3 and anim2 back
|
||||
gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 80 }, RunningOn.Compositor,
|
||||
"anim1 + anim2 + anim3, translate at 5s");
|
||||
// Bug 980769
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.3",
|
||||
"anim1 + anim2 + anim3, opacity at 5s");
|
||||
// seek past end of anim1
|
||||
advance_clock(5100);
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 0 }, RunningOn.MainThread,
|
||||
"anim1 + anim2 + anim3, translate at 10.1s");
|
||||
// Change the animation fill mode on the completed animation.
|
||||
gDiv.style.animation =
|
||||
"anim1 linear 10s forwards, anim2 linear 20s, anim3 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 100 }, RunningOn.MainThread,
|
||||
"anim1 + anim2 + anim3, translate at 10.1s with fill mode");
|
||||
advance_clock(900);
|
||||
omta_is("transform", { tx: 100 }, RunningOn.MainThread,
|
||||
"anim1 + anim2 + anim3, translate at 11s with fill mode");
|
||||
// Change the animation duration on the completed animation, so it is
|
||||
// no longer completed.
|
||||
// XXX Not sure about this---there seems to be a bug in test_animations.html
|
||||
// in that it drops the fill mode but the test comment says it has a fill mode
|
||||
gDiv.style.animation = "anim1 linear 20s, anim2 linear 20s, anim3 linear 10s";
|
||||
yield waitForPaintsFlushed();
|
||||
omta_is("transform", { tx: 82 }, RunningOn.Compositor,
|
||||
"anim1 + anim2 + anim3, translate at 11s with fill mode");
|
||||
// Bug 980769 - We should get 0.9 but instead
|
||||
todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.9",
|
||||
"anim1 + anim2 + anim3, opacity at 11s");
|
||||
done_div();
|
||||
});
|
||||
|
||||
/*
|
||||
* css3-animations: 3. Keyframes
|
||||
* http://dev.w3.org/csswg/css3-animations/#keyframes
|
||||
*/
|
||||
|
||||
// Test the rules on keyframes that lack a 0% or 100% rule:
|
||||
// (simultaneously, test that reverse animations have their keyframes
|
||||
// run backwards)
|
||||
|
||||
addAsyncTest(function *() {
|
||||
// 100px at 0%, 50px at 50%, 150px at 100%
|
||||
new_div("transform: translate(100px); " +
|
||||
"animation: kf1 ease 1s alternate infinite");
|
||||
advance_clock(0);
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 0.0s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 0.1s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 0.3s");
|
||||
advance_clock(200);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-0% at 0.5s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.4) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 0.7s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 0.9s");
|
||||
advance_clock(100);
|
||||
omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-0% at 1.0s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 1.1s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 1.4s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 1.7s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0% at 1.9s");
|
||||
advance_clock(100);
|
||||
omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 2.0s");
|
||||
done_div();
|
||||
|
||||
// 150px at 0%, 50px at 50%, 100px at 100%
|
||||
new_div("transform: translate(100px); " +
|
||||
"animation: kf2 ease-in 1s alternate infinite");
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-100% at 0.0s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 0.1s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 0.3s");
|
||||
advance_clock(200);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-100% at 0.5s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.4) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 0.7s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 0.9s");
|
||||
advance_clock(100);
|
||||
omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-100% at 1.0s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 1.1s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 1.4s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 1.7s");
|
||||
advance_clock(200);
|
||||
omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-100% at 1.9s");
|
||||
advance_clock(100);
|
||||
omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-100% at 2.0s");
|
||||
done_div();
|
||||
|
||||
// 50px at 0%, 100px at 25%, 50px at 100%
|
||||
new_div("transform: translate(50px); " +
|
||||
"animation: kf3 ease-out 1s alternate infinite");
|
||||
yield waitForPaints();
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor,
|
||||
"no-0%-no-100% at 0.0s");
|
||||
advance_clock(50);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.05s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.15s");
|
||||
advance_clock(100);
|
||||
omta_is("transform", { tx: "100px" }, RunningOn.Compositor,
|
||||
"no-0%-no-100% at 0.25s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.4) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.55s");
|
||||
advance_clock(300);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.85s");
|
||||
advance_clock(150);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor,
|
||||
"no-0%-no-100% at 1.0s");
|
||||
advance_clock(150);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.8) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.15s");
|
||||
advance_clock(450);
|
||||
omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.6s");
|
||||
advance_clock(250);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.6) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.85s");
|
||||
advance_clock(100);
|
||||
omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.2) },
|
||||
RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.95s");
|
||||
advance_clock(50);
|
||||
omta_is("transform", { tx: 50 }, RunningOn.Compositor,
|
||||
"no-0%-no-100% at 2.0s");
|
||||
done_div();
|
||||
|
||||
// Test that non-animatable properties are ignored.
|
||||
// Simultaneously, test that the block is still honored, and that
|
||||
// we still override the value when two consecutive keyframes have
|
||||
// the same value.
|
||||
new_div("animation: kf4 ease 10s");
|
||||
yield waitForPaints();
|
||||
var cs = window.getComputedStyle(gDiv);
|
||||
is(cs.display, "block",
|
||||
"non-animatable properties should be ignored (linear, 0s)");
|
||||
omta_is("transform", { tx: 37 }, RunningOn.Compositor,
|
||||
"animatable properties should still apply (linear, 0s)");
|
||||
advance_clock(1000);
|
||||
is(cs.display, "block",
|
||||
"non-animatable properties should be ignored (linear, 1s)");
|
||||
omta_is("transform", { tx: 37 }, RunningOn.Compositor,
|
||||
"animatable properties should still apply (linear, 1s)");
|
||||
done_div();
|
||||
new_div("animation: kf4 step-start 10s");
|
||||
yield waitForPaints();
|
||||
cs = window.getComputedStyle(gDiv);
|
||||
is(cs.display, "block",
|
||||
"non-animatable properties should be ignored (step-start, 0s)");
|
||||
omta_is("transform", { tx: 37 }, RunningOn.Compositor,
|
||||
"animatable properties should still apply (step-start, 0s)");
|
||||
advance_clock(1000);
|
||||
is(cs.display, "block",
|
||||
"non-animatable properties should be ignored (step-start, 1s)");
|
||||
omta_is("transform", { tx: 37 }, RunningOn.Compositor,
|
||||
"animatable properties should still apply (step-start, 1s)");
|
||||
done_div();
|
||||
|
||||
// Test cascading of the keyframes within an @keyframes rule.
|
||||
new_div("animation: kf_cascade1 linear 10s");
|
||||
yield waitForPaints();
|
||||
// 0%: 30px
|
||||
// 50%: 20px
|
||||
// 75%: 20px
|
||||
// 85%: 30px
|
||||
// 85.1%: 60px
|
||||
// 100%: 70px
|
||||
omta_is("transform", { tx: 30 }, RunningOn.Compositor, "kf_cascade1 at 0s");
|
||||
advance_clock(2500);
|
||||
omta_is("transform", { tx: 25 }, RunningOn.Compositor, "kf_cascade1 at 2.5s");
|
||||
advance_clock(2500);
|
||||
omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 5s");
|
||||
advance_clock(2000);
|
||||
omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 7s");
|
||||
advance_clock(500);
|
||||
omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 7.5s");
|
||||
advance_clock(500);
|
||||
omta_is("transform", { tx: 25 }, RunningOn.Compositor, "kf_cascade1 at 8s");
|
||||
advance_clock(500);
|
||||
omta_is("transform", { tx: 30 }, RunningOn.Compositor, "kf_cascade1 at 8.5s");
|
||||
advance_clock(10);
|
||||
// For some reason we get an error of 0.0003 for this test only
|
||||
omta_is_approx("transform", { tx: 60 }, RunningOn.Compositor, 0.001,
|
||||
"kf_cascade1 at 8.51s");
|
||||
advance_clock(745);
|
||||
omta_is("transform", { tx: 65 }, RunningOn.Compositor,
|
||||
"kf_cascade1 at 9.2505s");
|
||||
done_div();
|
||||
|
||||
// Test cascading of the @keyframes rules themselves.
|
||||
new_div("animation: kf_cascade2 linear 10s");
|
||||
yield waitForPaints();
|
||||
omta_is("opacity", 1, RunningOn.MainThread,
|
||||
"last @keyframes rule with transform should win");
|
||||
omta_is("transform", { tx: 100 }, RunningOn.Compositor,
|
||||
"last @keyframes rule with transform should win");
|
||||
done_div();
|
||||
});
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// Helper functions from test_animations.html
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
function new_div(style) {
|
||||
if (gDiv !== null) {
|
||||
ok(false, "test author forgot to call done_div");
|
||||
}
|
||||
if (typeof(style) != "string") {
|
||||
ok(false, "test author forgot to pass style argument");
|
||||
}
|
||||
gDiv = document.createElement("div");
|
||||
gDiv.classList.add("target");
|
||||
gDiv.setAttribute("style", style);
|
||||
gDisplay.appendChild(gDiv);
|
||||
gDiv.clientTop;
|
||||
}
|
||||
|
||||
function done_div() {
|
||||
if (gDiv === null) {
|
||||
ok(false, "test author forgot to call new_div");
|
||||
}
|
||||
gDisplay.removeChild(gDiv);
|
||||
gDiv = null;
|
||||
}
|
||||
|
||||
function listen() {
|
||||
gEventsReceived = [];
|
||||
function listener(event) {
|
||||
gEventsReceived.push(event);
|
||||
}
|
||||
gDiv.addEventListener("animationstart", listener, false);
|
||||
gDiv.addEventListener("animationiteration", listener, false);
|
||||
gDiv.addEventListener("animationend", listener, false);
|
||||
}
|
||||
|
||||
function check_events(events_expected, desc) {
|
||||
// This function checks that the list of events_expected matches
|
||||
// the received events -- but it only checks the properties that
|
||||
// are present on events_expected.
|
||||
is(gEventsReceived.length, events_expected.length,
|
||||
"number of events received for " + desc);
|
||||
for (var i = 0,
|
||||
i_end = Math.min(events_expected.length, gEventsReceived.length);
|
||||
i != i_end; ++i) {
|
||||
var exp = events_expected[i];
|
||||
var rec = gEventsReceived[i];
|
||||
for (var prop in exp) {
|
||||
if (prop == "elapsedTime") {
|
||||
// Allow floating point error.
|
||||
ok(Math.abs(rec.elapsedTime - exp.elapsedTime) < 0.000002,
|
||||
"events[" + i + "]." + prop + " for " + desc +
|
||||
" received=" + rec.elapsedTime + " expected=" + exp.elapsedTime);
|
||||
} else {
|
||||
is(rec[prop], exp[prop], "events[" + i + "]." + prop + " for " + desc);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i = events_expected.length; i < gEventsReceived.length; ++i) {
|
||||
ok(false, "unexpected " + gEventsReceived[i].type + " event for " + desc);
|
||||
}
|
||||
gEventsReceived = [];
|
||||
}
|
||||
|
||||
function advance_clock(milliseconds) {
|
||||
SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(milliseconds);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// Helper functions for querying the compositor thread
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
// Returns a Promise that resolves once all paints have completed
|
||||
function waitForPaints() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitForAllPaints(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
// As with waitForPaints but also flushes pending style changes before waiting
|
||||
function waitForPaintsFlushed() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
waitForAllPaintsFlushed(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//
|
||||
// Helper functions for working with animated values
|
||||
//
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
const RunningOn = {
|
||||
MainThread: 0,
|
||||
Compositor: 1,
|
||||
Either: 2
|
||||
};
|
||||
|
||||
function omta_is(property, expected, runningOn, desc) {
|
||||
return omta_is_approx(property, expected, runningOn, 0, desc);
|
||||
}
|
||||
|
||||
function omta_is_approx(property, expected, runningOn, tolerance, desc) {
|
||||
// Check input
|
||||
const omtaProperties = [ "transform", "opacity" ];
|
||||
if (omtaProperties.indexOf(property) === -1) {
|
||||
ok(false, property + " is not an OMTA property");
|
||||
return;
|
||||
}
|
||||
var isTransform = property == "transform";
|
||||
var normalize = isTransform ? convertTo3dMatrix : parseFloat;
|
||||
var compare = isTransform ?
|
||||
matricesRoughlyEqual :
|
||||
function(a, b, error) { return Math.abs(a - b) <= error; };
|
||||
var normalizedToString = isTransform ?
|
||||
convert3dMatrixToString :
|
||||
JSON.stringify;
|
||||
|
||||
// Get actual values
|
||||
var compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, property);
|
||||
var computedStr = window.getComputedStyle(gDiv)[property];
|
||||
|
||||
// Prepare expected value
|
||||
var expectedValue = normalize(expected);
|
||||
if (expectedValue === null) {
|
||||
ok(false, desc + ": test author should provide a valid 'expected' value" +
|
||||
" - got " + expected.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check expected value appears in the right place
|
||||
var actualStr;
|
||||
switch (runningOn) {
|
||||
case RunningOn.Either:
|
||||
runningOn = compositorStr !== "" ?
|
||||
RunningOn.Compositor :
|
||||
RunningOn.MainThread;
|
||||
actualStr = compositorStr !== "" ? compositorStr : computedStr;
|
||||
break;
|
||||
|
||||
case RunningOn.Compositor:
|
||||
if (compositorStr === "") {
|
||||
ok(false, desc + ": should be animating on compositor");
|
||||
return;
|
||||
}
|
||||
actualStr = compositorStr;
|
||||
break;
|
||||
|
||||
default:
|
||||
if (compositorStr !== "") {
|
||||
ok(false, desc + ": should NOT be animating on compositor");
|
||||
return;
|
||||
}
|
||||
actualStr = computedStr;
|
||||
break;
|
||||
}
|
||||
|
||||
// Compare animated value with expected
|
||||
var actualValue = normalize(actualStr);
|
||||
if (actualValue === null) {
|
||||
ok(false, desc + ": should return a valid result - got " + actualStr);
|
||||
return;
|
||||
}
|
||||
ok(compare(expectedValue, actualValue, tolerance),
|
||||
desc + " - got " + actualStr + ", expected " +
|
||||
normalizedToString(expectedValue));
|
||||
|
||||
// For compositor animations do an additional check that they match
|
||||
// the value calculated on the main thread
|
||||
if (runningOn === RunningOn.Compositor) {
|
||||
var computedValue = normalize(computedStr);
|
||||
if (computedValue === null) {
|
||||
ok(false, desc + ": test framework should parse computed style" +
|
||||
" - got " + computedStr);
|
||||
return;
|
||||
}
|
||||
ok(compare(computedValue, actualValue, 0),
|
||||
desc + ": OMTA style and computed style should be equal" +
|
||||
" - OMTA " + actualStr + ", computed " + computedStr);
|
||||
}
|
||||
}
|
||||
|
||||
function matricesRoughlyEqual(a, b, tolerance) {
|
||||
tolerance = tolerance || 0.0001;
|
||||
for (var i = 0; i < 4; i++) {
|
||||
for (var j = 0; j < 4; j++) {
|
||||
if (Math.abs(a[i][j] - b[i][j]) > tolerance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Converts something representing an transform into a 3d matrix in column-major
|
||||
// order.
|
||||
// The following are supported:
|
||||
// "matrix(...)"
|
||||
// "matrix3d(...)"
|
||||
// [ 1, 0, 0, ... ]
|
||||
// { a: 1, ty: 23 } etc.
|
||||
function convertTo3dMatrix(matrixLike) {
|
||||
if (typeof(matrixLike) == "string") {
|
||||
return convertStringTo3dMatrix(matrixLike);
|
||||
} else if (Array.isArray(matrixLike)) {
|
||||
return convertArrayTo3dMatrix(matrixLike);
|
||||
} else if (typeof(matrixLike) == "object") {
|
||||
return convertObjectTo3dMatrix(matrixLike);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Converts strings of the format "matrix(...)" and "matrix3d(...)" to a 3d
|
||||
// matrix
|
||||
function convertStringTo3dMatrix(str) {
|
||||
if (str == "none")
|
||||
return convertArrayTo3dMatrix([1, 0, 0, 1, 0, 0]);
|
||||
var result = str.match("^matrix(3d)?\\(");
|
||||
if (result === null)
|
||||
return null;
|
||||
|
||||
return convertArrayTo3dMatrix(
|
||||
str.substring(result[0].length, str.length-1)
|
||||
.split(",")
|
||||
.map(function(component) {
|
||||
return Number(component);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
// Takes an array of numbers of length 6 (2d matrix) or 16 (3d matrix)
|
||||
// representing a matrix specified in column-major order and returns a 3d matrix
|
||||
// represented as an array of arrays
|
||||
function convertArrayTo3dMatrix(array) {
|
||||
if (array.length == 6) {
|
||||
return convertObjectTo3dMatrix(
|
||||
{ a: array[0], b: array[1],
|
||||
c: array[2], d: array[3],
|
||||
e: array[4], f: array[5] } );
|
||||
} else if (array.length == 16) {
|
||||
return [
|
||||
array.slice(0, 3),
|
||||
array.slice(4, 7),
|
||||
array.slice(8, 11),
|
||||
array.slice(12, 15)
|
||||
];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Takes an object of the form { a: 1.1, e: 23 } and builds up a 3d matrix
|
||||
// with unspecified values filled in with identity values.
|
||||
function convertObjectTo3dMatrix(obj) {
|
||||
return [
|
||||
[
|
||||
obj.a || obj.sx || obj.m11 || 1,
|
||||
obj.b || obj.m12 || 0,
|
||||
obj.m13 || 0,
|
||||
obj.m14 || 0
|
||||
], [
|
||||
obj.c || obj.m21 || 0,
|
||||
obj.d || obj.sy || obj.m22 || 1,
|
||||
obj.m23 || 0,
|
||||
obj.m24 || 0
|
||||
], [
|
||||
obj.m31 || 0,
|
||||
obj.m32 || 0,
|
||||
obj.sz || obj.m33 || 1,
|
||||
obj.m34 || 0
|
||||
], [
|
||||
obj.e || obj.tx || obj.m41 || 0,
|
||||
obj.f || obj.ty || obj.m42 || 0,
|
||||
obj.tz || obj.m43 || 0,
|
||||
obj.m44 || 1
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
function convert3dMatrixToString(matrix) {
|
||||
if (is2d(matrix)) {
|
||||
return "matrix(" +
|
||||
[ matrix[0][0], matrix[0][1],
|
||||
matrix[1][0], matrix[1][1],
|
||||
matrix[3][0], matrix[3][1] ].join(", ") + ")";
|
||||
} else {
|
||||
return "matrix3d(" +
|
||||
matrix.reduce(function(outer, inner) {
|
||||
return outer.concat(inner);
|
||||
}).join(", ") + ")";
|
||||
}
|
||||
}
|
||||
|
||||
function is2d(matrix) {
|
||||
return matrix[0][2] === 0 && matrix[0][3] === 0 &&
|
||||
matrix[1][2] === 0 && matrix[1][3] === 0 &&
|
||||
matrix[2][0] === 0 && matrix[2][1] === 0 &&
|
||||
matrix[2][2] === 1 && matrix[2][3] === 0 &&
|
||||
matrix[3][2] === 0 && matrix[3][3] === 1;
|
||||
}
|
||||
</script>
|
||||
</html>
|
|
@ -10,6 +10,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=975261
|
|||
src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="/tests/SimpleTest/paint_listener.js"></script>
|
||||
<script type="application/javascript" src="animation_utils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style type="text/css">
|
||||
@keyframes anim-opacity {
|
||||
|
@ -48,15 +49,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=975261
|
|||
"use strict";
|
||||
|
||||
var gUtils = SpecialPowers.DOMWindowUtils;
|
||||
var gOMTAPrefKey = "layers.offmainthreadcomposition.async-animations";
|
||||
var gOMTCEnabled = gUtils.layerManagerRemote;
|
||||
|
||||
if (gOMTCEnabled && SpecialPowers.getBoolPref(gOMTAPrefKey)) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.addEventListener("load", testDelay);
|
||||
} else {
|
||||
ok(true, "OMTA not available");
|
||||
}
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runOMTATest(testDelay, SimpleTest.finish);
|
||||
|
||||
function newTarget() {
|
||||
var target = document.createElement("div");
|
||||
|
|
|
@ -727,7 +727,12 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
|
|||
mLayerRendererInitialized = true;
|
||||
}
|
||||
|
||||
return mLayerRenderer.createFrame(mFrameMetrics);
|
||||
try {
|
||||
return mLayerRenderer.createFrame(mFrameMetrics);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOGTAG, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@WrapElementForJNI(allowMultithread = true)
|
||||
|
|
|
@ -326,6 +326,9 @@ public class LayerRenderer implements Tabs.OnTabsChangedListener {
|
|||
}
|
||||
|
||||
private RenderContext createContext(RectF viewport, RectF pageRect, float zoomFactor, PointF offset) {
|
||||
if (mCoordBuffer == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return new RenderContext(viewport, pageRect, zoomFactor, offset, mPositionHandle, mTextureHandle,
|
||||
mCoordBuffer);
|
||||
}
|
||||
|
|
|
@ -335,6 +335,9 @@ pref("apz.axis_lock_mode", 0);
|
|||
// Whether to print the APZC tree for debugging
|
||||
pref("apz.printtree", false);
|
||||
|
||||
// Layerize scrollable subframes to allow async panning
|
||||
pref("apz.subframe.enabled", false);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// Whether to run in native HiDPI mode on machines with "Retina"/HiDPI display;
|
||||
// <= 0 : hidpi mode disabled, display will just use pixel-based upscaling
|
||||
|
|
|
@ -314,6 +314,10 @@ Http2Stream::ParseHttpRequestHeaders(const char *buf,
|
|||
if (cache)
|
||||
pushedStream = cache->RemovePushedStreamHttp2(hashkey);
|
||||
|
||||
LOG3(("Pushed Stream Lookup "
|
||||
"session=%p key=%s loadgroupci=%p cache=%p hit=%p\n",
|
||||
mSession, hashkey.get(), loadGroupCI, cache, pushedStream));
|
||||
|
||||
if (pushedStream) {
|
||||
LOG3(("Pushed Stream Match located id=0x%X key=%s\n",
|
||||
pushedStream->StreamID(), hashkey.get()));
|
||||
|
|
|
@ -733,10 +733,6 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
|
|||
|
||||
pushBuffer.AppendLiteral("\" href=\"");
|
||||
|
||||
nsXPIDLCString encoding;
|
||||
rv = mParser->GetEncoding(getter_Copies(encoding));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
// need to escape links
|
||||
nsAutoCString locEscaped;
|
||||
|
||||
|
@ -754,26 +750,20 @@ nsIndexedToHTML::OnIndexAvailable(nsIRequest *aRequest,
|
|||
if (mExpectAbsLoc &&
|
||||
NS_SUCCEEDED(net_ExtractURLScheme(loc, nullptr, nullptr, nullptr))) {
|
||||
// escape as absolute
|
||||
escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_Minimal;
|
||||
escFlags = esc_Forced | esc_AlwaysCopy | esc_Minimal;
|
||||
}
|
||||
else {
|
||||
// escape as relative
|
||||
// esc_Directory is needed because directories have a trailing slash.
|
||||
// Without it, the trailing '/' will be escaped, and links from within
|
||||
// that directory will be incorrect
|
||||
escFlags = esc_Forced | esc_OnlyASCII | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
|
||||
escFlags = esc_Forced | esc_AlwaysCopy | esc_FileBaseName | esc_Colon | esc_Directory;
|
||||
}
|
||||
NS_EscapeURL(loc.get(), loc.Length(), escFlags, locEscaped);
|
||||
// esc_Directory does not escape the semicolons, so if a filename
|
||||
// contains semicolons we need to manually escape them.
|
||||
// This replacement should be removed in bug #473280
|
||||
locEscaped.ReplaceSubstring(";", "%3b");
|
||||
if (!encoding.EqualsLiteral("UTF-8")) {
|
||||
// Escape all non-ASCII bytes to preserve the raw value.
|
||||
nsAutoCString outstr;
|
||||
NS_EscapeURL(locEscaped, esc_AlwaysCopy | esc_OnlyNonASCII, outstr);
|
||||
locEscaped = outstr;
|
||||
}
|
||||
nsAdoptingCString htmlEscapedURL(nsEscapeHTML(locEscaped.get()));
|
||||
pushBuffer.Append(htmlEscapedURL);
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
NSPR_4_10_5_BETA2
|
||||
NSPR_4_10_5_BETA3
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
dnl
|
||||
dnl Local autoconf macros used with Mozilla.
|
||||
dnl The contents of this file are under the Public Domain.
|
||||
dnl
|
||||
|
||||
builtin(include, build/autoconf/acwinpaths.m4)
|
|
@ -1,13 +0,0 @@
|
|||
dnl This Source Code Form is subject to the terms of the Mozilla Public
|
||||
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
dnl Work around the problem that a DOS-style absolute path is split by
|
||||
dnl the colon in autoconf 2.59 and earlier. The _AC_OUTPUT_FILES macro
|
||||
dnl was removed and the problem was fixed in autoconf 2.60.
|
||||
define(GENERATE_FILES_NOSPLIT, [
|
||||
define([_AC_OUTPUT_FILES],
|
||||
[patsubst($@, [`IFS=:], [`#IFS=:])])
|
||||
])
|
||||
m4_ifdef([_AC_OUTPUT_FILES],
|
||||
[GENERATE_FILES_NOSPLIT(defn([_AC_OUTPUT_FILES]))])
|
|
@ -10,3 +10,4 @@
|
|||
*/
|
||||
|
||||
#error "Do not include this header file."
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,11 +4,12 @@ dnl This Source Code Form is subject to the terms of the Mozilla Public
|
|||
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
AC_PREREQ(2.56)
|
||||
AC_INIT(config/libc_r.h)
|
||||
AC_PREREQ(2.61)
|
||||
AC_INIT
|
||||
AC_CONFIG_SRCDIR([pr/include/nspr.h])
|
||||
|
||||
AC_CONFIG_AUX_DIR(${srcdir}/build/autoconf)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AC_CANONICAL_TARGET
|
||||
|
||||
dnl ========================================================
|
||||
dnl = Defaults
|
||||
|
@ -673,7 +674,6 @@ else
|
|||
AC_PROG_CXX
|
||||
fi
|
||||
fi
|
||||
AC_PROG_CPP
|
||||
AC_PROG_RANLIB
|
||||
AC_PATH_PROGS(AS, as, $CC)
|
||||
AC_PATH_PROGS(AR, ar, echo not_ar)
|
||||
|
@ -688,6 +688,8 @@ else
|
|||
fi
|
||||
fi
|
||||
|
||||
AC_PROG_CPP
|
||||
|
||||
if test "$GCC" = "yes"; then
|
||||
GNU_CC=1
|
||||
fi
|
||||
|
@ -2530,29 +2532,6 @@ if test -z "$SKIP_LIBRARY_CHECKS"; then
|
|||
dnl ========================================================
|
||||
dnl Check for system libraries
|
||||
dnl ========================================================
|
||||
dnl AC_CHECK_LIB(C, main)
|
||||
dnl AC_CHECK_LIB(C_r, main)
|
||||
dnl AC_CHECK_LIB(c, main)
|
||||
dnl AC_CHECK_LIB(c_r, main)
|
||||
dnl AC_CHECK_LIB(dce, main)
|
||||
dnl AC_CHECK_LIB(dl, main)
|
||||
dnl AC_CHECK_LIB(dld, main)
|
||||
dnl AC_CHECK_LIB(gen, main)
|
||||
dnl AC_CHECK_LIB(ip6, main)
|
||||
dnl AC_CHECK_LIB(l, main)
|
||||
dnl AC_CHECK_LIB(m, main)
|
||||
dnl AC_CHECK_LIB(nsl, main)
|
||||
dnl AC_CHECK_LIB(posix4, main)
|
||||
dnl AC_CHECK_LIB(prstrms, main)
|
||||
dnl AC_CHECK_LIB(prstrms_shr, main)
|
||||
dnl AC_CHECK_LIB(pthread, main)
|
||||
dnl AC_CHECK_LIB(pthreads, main)
|
||||
dnl AC_CHECK_LIB(resolv, main)
|
||||
dnl AC_CHECK_LIB(rt, main)
|
||||
dnl AC_CHECK_LIB(socket, main)
|
||||
dnl AC_CHECK_LIB(svld, main)
|
||||
dnl AC_CHECK_LIB(thread, main)
|
||||
dnl AC_CHECK_LIB(vms_jackets, main)
|
||||
|
||||
|
||||
dnl We don't want anything to link with libdl even if it's present on OS X,
|
||||
|
@ -2574,25 +2553,10 @@ esac
|
|||
dnl ========================================================
|
||||
dnl Check for system header files.
|
||||
dnl ========================================================
|
||||
dnl AC_HEADER_DIRENT
|
||||
dnl AC_HEADER_STDC
|
||||
dnl AC_HEADER_SYS_WAIT
|
||||
dnl AC_CHECK_HEADERS(fcntl.h limits.h sys/file.h sys/ioctl.h sys/time.h unistd.h)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check for typedefs and structs
|
||||
dnl ========================================================
|
||||
dnl AC_C_CONST
|
||||
dnl AC_TYPE_UID_T
|
||||
dnl AC_TYPE_MODE_T
|
||||
dnl AC_TYPE_OFF_T
|
||||
dnl AC_TYPE_PID_T
|
||||
dnl AC_TYPE_SIZE_T
|
||||
dnl AC_STRUCT_ST_BLKSIZE
|
||||
dnl AC_STRUCT_ST_BLOCKS
|
||||
dnl AC_STRUCT_ST_RDEV
|
||||
dnl AC_HEADER_TIME
|
||||
dnl AC_STRUCT_TM
|
||||
|
||||
dnl ========================================================
|
||||
dnl Checks for library functions.
|
||||
|
@ -2603,15 +2567,6 @@ LIBS="$LIBS $OS_LIBS"
|
|||
AC_CHECK_FUNCS(dladdr gettid lchown setpriority strerror syscall)
|
||||
LIBS="$_SAVE_LIBS"
|
||||
|
||||
dnl AC_FUNC_MEMCMP
|
||||
dnl AC_FUNC_MMAP
|
||||
dnl AC_FUNC_SETVBUF_REVERSED
|
||||
dnl AC_FUNC_STRCOLL
|
||||
dnl AC_FUNC_STRFTIME
|
||||
dnl AC_FUNC_UTIME_NULL
|
||||
dnl AC_FUNC_VPRINTF
|
||||
dnl AC_CHECK_FUNCS(ftime getcwd gethostname gettimeofday getwd mkdir mktime putenv rmdir select socket strdup strerror strstr strtol strtoul uname)
|
||||
|
||||
dnl ========================================================
|
||||
dnl Check options
|
||||
dnl ========================================================
|
||||
|
@ -3272,4 +3227,6 @@ fi
|
|||
|
||||
echo $MAKEFILES > unallmakefiles
|
||||
|
||||
AC_OUTPUT([$MAKEFILES], [chmod +x config/nspr-config])
|
||||
AC_CONFIG_FILES([$MAKEFILES])
|
||||
AC_CONFIG_COMMANDS([default], [chmod +x config/nspr-config])
|
||||
AC_OUTPUT
|
||||
|
|
|
@ -136,6 +136,18 @@ extern PRInt32 _PR_ppc_AtomicSet(PRInt32 *val, PRInt32 newval);
|
|||
#define _MD_ATOMIC_SET _PR_ppc_AtomicSet
|
||||
#endif
|
||||
|
||||
#if defined(__powerpc64__)
|
||||
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
|
||||
/* Use GCC built-in functions */
|
||||
#define _PR_HAVE_ATOMIC_OPS
|
||||
#define _MD_INIT_ATOMIC()
|
||||
#define _MD_ATOMIC_INCREMENT(ptr) __sync_add_and_fetch(ptr, 1)
|
||||
#define _MD_ATOMIC_DECREMENT(ptr) __sync_sub_and_fetch(ptr, 1)
|
||||
#define _MD_ATOMIC_ADD(ptr, i) __sync_add_and_fetch(ptr, i)
|
||||
#define _MD_ATOMIC_SET(ptr, nv) __sync_lock_test_and_set(ptr, nv)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__alpha)
|
||||
#define _PR_HAVE_ATOMIC_OPS
|
||||
#define _MD_INIT_ATOMIC()
|
||||
|
|
|
@ -107,7 +107,7 @@ long __cdecl _InterlockedExchangeAdd(long volatile *Addend, long Value);
|
|||
((defined(__i386__) && \
|
||||
defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \
|
||||
defined(__ia64__) || defined(__x86_64__) || \
|
||||
(defined(__powerpc__) && !defined(__powerpc64__)) || \
|
||||
defined(__powerpc__) || \
|
||||
(defined(__arm__) && \
|
||||
defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4)) || \
|
||||
defined(__aarch64__) || defined(__alpha))))
|
||||
|
|
|
@ -90,7 +90,6 @@ _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
|
|||
{
|
||||
case WAIT_OBJECT_0:
|
||||
return PR_SUCCESS;
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
_PR_THREAD_LOCK(thread);
|
||||
if (thread->state == _PR_IO_WAIT) {
|
||||
|
@ -110,10 +109,8 @@ _PR_MD_WAIT(PRThread *thread, PRIntervalTime ticks)
|
|||
}
|
||||
}
|
||||
return PR_SUCCESS;
|
||||
break;
|
||||
default:
|
||||
return PR_FAILURE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PRStatus
|
||||
|
|
|
@ -791,7 +791,13 @@ this.DownloadIntegration = {
|
|||
|
||||
if (this.dontOpenFileAndFolder) {
|
||||
deferred.then((value) => { this._deferTestShowDir.resolve("success"); },
|
||||
(error) => { this._deferTestShowDir.reject(error); });
|
||||
(error) => {
|
||||
// Ensure that _deferTestShowDir has at least one consumer
|
||||
// for the error, otherwise the error will be reported as
|
||||
// uncaught.
|
||||
this._deferTestShowDir.promise.then(null, function() {});
|
||||
this._deferTestShowDir.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
return deferred;
|
||||
|
|
|
@ -2396,9 +2396,8 @@ nsWindow::DrawWindowUnderlay(LayerManagerComposite* aManager, nsIntRect aRect)
|
|||
}
|
||||
|
||||
jobject frameObj = client->CreateFrame();
|
||||
NS_ABORT_IF_FALSE(frameObj, "No frame object!");
|
||||
if (!frameObj) {
|
||||
ALOG_BRIDGE("Exceptional Exit: %s", __PRETTY_FUNCTION__);
|
||||
NS_WARNING("Warning: unable to obtain a LayerRenderer frame; aborting window underlay draw");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2425,8 +2424,10 @@ nsWindow::DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect)
|
|||
|
||||
AutoLocalJNIFrame jniFrame(env);
|
||||
|
||||
NS_ABORT_IF_FALSE(!mLayerRendererFrame.isNull(),
|
||||
"Frame should have been created in DrawWindowUnderlay()!");
|
||||
if (mLayerRendererFrame.isNull()) {
|
||||
NS_WARNING("Warning: do not have a LayerRenderer frame; aborting window overlay draw");
|
||||
return;
|
||||
}
|
||||
|
||||
mozilla::widget::android::GeckoLayerClient* client = AndroidBridge::Bridge()->GetLayerClient();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче