mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
Merge remote-tracking branch 'origin/master' into kmeleon76
This commit is contained in:
@@ -759,7 +759,7 @@ getAttributesCB(AtkObject *aAtkObj)
|
||||
if (!proxy)
|
||||
return nullptr;
|
||||
|
||||
nsAutoTArray<Attribute, 10> attrs;
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->Attributes(&attrs);
|
||||
if (attrs.IsEmpty())
|
||||
return nullptr;
|
||||
@@ -1019,7 +1019,7 @@ refRelationSetCB(AtkObject *aAtkObj)
|
||||
continue;
|
||||
|
||||
size_t targetCount = targetSets[i].Length();
|
||||
nsAutoTArray<AtkObject*, 5> wrappers;
|
||||
AutoTArray<AtkObject*, 5> wrappers;
|
||||
for (size_t j = 0; j < targetCount; j++)
|
||||
wrappers.AppendElement(GetWrapperFor(targetSets[i][j]));
|
||||
|
||||
@@ -1664,7 +1664,7 @@ AccessibleWrap::GetColumnHeader(TableAccessible* aAccessible, int32_t aColIdx)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
tableCell->ColHeaderCells(&headerCells);
|
||||
if (headerCells.IsEmpty()) {
|
||||
return nullptr;
|
||||
@@ -1698,7 +1698,7 @@ AccessibleWrap::GetRowHeader(TableAccessible* aAccessible, int32_t aRowIdx)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
tableCell->RowHeaderCells(&headerCells);
|
||||
if (headerCells.IsEmpty()) {
|
||||
return nullptr;
|
||||
|
||||
@@ -273,7 +273,7 @@ getSelectedColumnsCB(AtkTable *aTable, gint** aSelected)
|
||||
{
|
||||
*aSelected = nullptr;
|
||||
|
||||
nsAutoTArray<uint32_t, 10> cols;
|
||||
AutoTArray<uint32_t, 10> cols;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->SelectedColIndices(&cols);
|
||||
@@ -300,7 +300,7 @@ getSelectedColumnsCB(AtkTable *aTable, gint** aSelected)
|
||||
static gint
|
||||
getSelectedRowsCB(AtkTable *aTable, gint **aSelected)
|
||||
{
|
||||
nsAutoTArray<uint32_t, 10> rows;
|
||||
AutoTArray<uint32_t, 10> rows;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->SelectedRowIndices(&rows);
|
||||
|
||||
@@ -311,7 +311,7 @@ getRunAttributesCB(AtkText *aText, gint aOffset,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoTArray<Attribute, 10> attrs;
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->TextAttributes(false, aOffset, &attrs, &startOffset, &endOffset);
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
@@ -337,7 +337,7 @@ getDefaultAttributesCB(AtkText *aText)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoTArray<Attribute, 10> attrs;
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->DefaultTextAttributes(&attrs);
|
||||
return ConvertToAtkTextAttributeSet(attrs);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ using namespace mozilla::a11y;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments;
|
||||
nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
|
||||
DocManager::sRemoteXPCDocumentCache = nullptr;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager
|
||||
@@ -101,6 +103,16 @@ DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
|
||||
RemoveListeners(aDOMDocument);
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc)
|
||||
{
|
||||
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
|
||||
if (doc) {
|
||||
doc->Shutdown();
|
||||
sRemoteXPCDocumentCache->Remove(aDoc);
|
||||
}
|
||||
}
|
||||
|
||||
xpcAccessibleDocument*
|
||||
DocManager::GetXPCDocument(DocAccessible* aDocument)
|
||||
{
|
||||
@@ -115,6 +127,26 @@ DocManager::GetXPCDocument(DocAccessible* aDocument)
|
||||
return xpcDoc;
|
||||
}
|
||||
|
||||
xpcAccessibleDocument*
|
||||
DocManager::GetXPCDocument(DocAccessibleParent* aDoc)
|
||||
{
|
||||
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
|
||||
if (doc) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
if (!sRemoteXPCDocumentCache) {
|
||||
sRemoteXPCDocumentCache =
|
||||
new nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>;
|
||||
}
|
||||
|
||||
doc =
|
||||
new xpcAccessibleDocument(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
|
||||
sRemoteXPCDocumentCache->Put(aDoc, doc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
DocManager::IsProcessingRefreshDriverNotification() const
|
||||
|
||||
@@ -90,6 +90,21 @@ public:
|
||||
static const nsTArray<DocAccessibleParent*>* TopLevelRemoteDocs()
|
||||
{ return sRemoteDocuments; }
|
||||
|
||||
/**
|
||||
* Remove the xpc document for a remote document if there is one.
|
||||
*/
|
||||
static void NotifyOfRemoteDocShutdown(DocAccessibleParent* adoc);
|
||||
|
||||
/**
|
||||
* Get a XPC document for a remote document.
|
||||
*/
|
||||
static xpcAccessibleDocument* GetXPCDocument(DocAccessibleParent* aDoc);
|
||||
static xpcAccessibleDocument* GetCachedXPCDocument(const DocAccessibleParent* aDoc)
|
||||
{
|
||||
return sRemoteXPCDocumentCache ? sRemoteXPCDocumentCache->GetWeak(aDoc)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsProcessingRefreshDriverNotification() const;
|
||||
#endif
|
||||
@@ -147,6 +162,8 @@ private:
|
||||
typedef nsRefPtrHashtable<nsPtrHashKey<const DocAccessible>, xpcAccessibleDocument>
|
||||
XPCDocumentHashtable;
|
||||
XPCDocumentHashtable mXPCDocumentCache;
|
||||
static nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
|
||||
sRemoteXPCDocumentCache;
|
||||
|
||||
/*
|
||||
* The list of remote top level documents.
|
||||
|
||||
@@ -76,7 +76,7 @@ protected:
|
||||
DocAccessible* mDocument;
|
||||
|
||||
/**
|
||||
* Pending events array. Don't make this an nsAutoTArray; we use
|
||||
* Pending events array. Don't make this an AutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<AccEvent> > mEvents;
|
||||
|
||||
@@ -275,7 +275,7 @@ private:
|
||||
|
||||
/**
|
||||
* A pending accessible tree update notifications for content insertions.
|
||||
* Don't make this an nsAutoTArray; we use SwapElements() on it.
|
||||
* Don't make this an AutoTArray; we use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<ContentInsertion> > mContentInsertions;
|
||||
|
||||
@@ -309,7 +309,7 @@ private:
|
||||
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
|
||||
|
||||
/**
|
||||
* Other notifications like DOM events. Don't make this an nsAutoTArray; we
|
||||
* Other notifications like DOM events. Don't make this an AutoTArray; we
|
||||
* use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<Notification> > mNotifications;
|
||||
|
||||
@@ -17,7 +17,7 @@ inline Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
return CommonParent(mStartContainer, mEndContainer,
|
||||
&parents1, &pos1, &parents2, &pos2);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ TextPoint::operator <(const TextPoint& aPoint) const
|
||||
// Build the chain of parents
|
||||
Accessible* p1 = mContainer;
|
||||
Accessible* p2 = aPoint.mContainer;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
@@ -76,7 +76,7 @@ TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
||||
Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
Accessible* container =
|
||||
CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
|
||||
|
||||
@@ -146,7 +146,7 @@ bool
|
||||
TextRange::Crop(Accessible* aContainer)
|
||||
{
|
||||
uint32_t boundaryPos = 0, containerPos = 0;
|
||||
nsAutoTArray<Accessible*, 30> boundaryParents, containerParents;
|
||||
AutoTArray<Accessible*, 30> boundaryParents, containerParents;
|
||||
|
||||
// Crop the start boundary.
|
||||
Accessible* container = nullptr;
|
||||
|
||||
@@ -27,8 +27,8 @@ TreeWalker::
|
||||
{
|
||||
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
||||
|
||||
mChildFilter = mContext->CanHaveAnonChildren() ?
|
||||
nsIContent::eAllChildren : nsIContent::eAllButXBL;
|
||||
mChildFilter = mContext->NoXBLKids() ?
|
||||
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||
mChildFilter |= nsIContent::eSkipPlaceholderContent;
|
||||
|
||||
if (aContent)
|
||||
@@ -46,7 +46,7 @@ TreeWalker::~TreeWalker()
|
||||
// TreeWalker: private
|
||||
|
||||
Accessible*
|
||||
TreeWalker::NextChild()
|
||||
TreeWalker::Next()
|
||||
{
|
||||
if (mStateStack.IsEmpty())
|
||||
return nullptr;
|
||||
@@ -80,11 +80,9 @@ TreeWalker::NextChild()
|
||||
|
||||
nsIContent* parent = parentNode->AsElement();
|
||||
top = PushState(parent);
|
||||
while (nsIContent* childNode = Next(top)) {
|
||||
if (childNode == mAnchorNode) {
|
||||
mAnchorNode = parent;
|
||||
return NextChild();
|
||||
}
|
||||
if (top->mDOMIter.Seek(mAnchorNode)) {
|
||||
mAnchorNode = parent;
|
||||
return Next();
|
||||
}
|
||||
|
||||
// XXX We really should never get here, it means we're trying to find an
|
||||
|
||||
@@ -44,13 +44,13 @@ public:
|
||||
~TreeWalker();
|
||||
|
||||
/**
|
||||
* Return the next child accessible.
|
||||
* Return the next accessible.
|
||||
*
|
||||
* @note Returned accessible is bound to the document, if the accessible is
|
||||
* rejected during tree creation then the caller should be unbind it
|
||||
* from the document.
|
||||
*/
|
||||
Accessible* NextChild();
|
||||
Accessible* Next();
|
||||
|
||||
private:
|
||||
TreeWalker();
|
||||
@@ -87,7 +87,7 @@ private:
|
||||
DocAccessible* mDoc;
|
||||
Accessible* mContext;
|
||||
nsIContent* mAnchorNode;
|
||||
nsAutoTArray<ChildrenIterator, 20> mStateStack;
|
||||
AutoTArray<ChildrenIterator, 20> mStateStack;
|
||||
int32_t mChildFilter;
|
||||
uint32_t mFlags;
|
||||
};
|
||||
|
||||
@@ -596,7 +596,7 @@ nsAccessibilityService::ContentRemoved(nsIPresShell* aPresShell,
|
||||
Accessible* container = document->GetContainerAccessible(aChildNode);
|
||||
a11y::TreeWalker walker(container ? container : document, aChildNode,
|
||||
a11y::TreeWalker::eWalkCache);
|
||||
child = walker.NextChild();
|
||||
child = walker.Next();
|
||||
}
|
||||
|
||||
if (child) {
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
#include "nsIBoxObject.h"
|
||||
#include "nsIDOMXULElement.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIScrollableFrame.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
@@ -647,3 +649,29 @@ nsCoreUtils::IsWhitespaceString(const nsSubstring& aString)
|
||||
|
||||
return iterBegin == iterEnd;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCoreUtils::AccEventObserversExist()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obsService, false);
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> observers;
|
||||
obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC,
|
||||
getter_AddRefs(observers));
|
||||
NS_ENSURE_TRUE(observers, false);
|
||||
|
||||
bool hasObservers = false;
|
||||
observers->HasMoreElements(&hasObservers);
|
||||
|
||||
return hasObservers;
|
||||
}
|
||||
|
||||
void
|
||||
nsCoreUtils::DispatchAccEvent(RefPtr<nsIAccessibleEvent> event)
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
||||
NS_ENSURE_TRUE_VOID(obsService);
|
||||
|
||||
obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define nsCoreUtils_h_
|
||||
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "nsIAccessibleEvent.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h" // for GetShell()
|
||||
#include "nsIPresShell.h"
|
||||
@@ -314,6 +315,16 @@ public:
|
||||
return aChar == ' ' || aChar == '\n' ||
|
||||
aChar == '\r' || aChar == '\t' || aChar == 0xa0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if there are any observers of accessible events.
|
||||
*/
|
||||
static bool AccEventObserversExist();
|
||||
|
||||
/**
|
||||
* Notify accessible event observers of an event.
|
||||
*/
|
||||
static void DispatchAccEvent(RefPtr<nsIAccessibleEvent> aEvent);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -302,12 +302,6 @@ Accessible::KeyboardShortcut() const
|
||||
return KeyBinding();
|
||||
}
|
||||
|
||||
bool
|
||||
Accessible::CanHaveAnonChildren()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
|
||||
{
|
||||
@@ -885,20 +879,8 @@ Accessible::HandleAccEvent(AccEvent* aEvent)
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obsService, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> observers;
|
||||
obsService->EnumerateObservers(NS_ACCESSIBLE_EVENT_TOPIC,
|
||||
getter_AddRefs(observers));
|
||||
|
||||
NS_ENSURE_STATE(observers);
|
||||
|
||||
bool hasObservers = false;
|
||||
observers->HasMoreElements(&hasObservers);
|
||||
if (hasObservers) {
|
||||
nsCOMPtr<nsIAccessibleEvent> event = MakeXPCEvent(aEvent);
|
||||
return obsService->NotifyObservers(event, NS_ACCESSIBLE_EVENT_TOPIC, nullptr);
|
||||
if (nsCoreUtils::AccEventObserversExist()) {
|
||||
nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@@ -2512,7 +2494,7 @@ Accessible::CacheChildren()
|
||||
TreeWalker walker(this, mContent);
|
||||
|
||||
Accessible* child = nullptr;
|
||||
while ((child = walker.NextChild()) && AppendChild(child));
|
||||
while ((child = walker.Next()) && AppendChild(child));
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -489,11 +489,6 @@ public:
|
||||
*/
|
||||
virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
|
||||
|
||||
/**
|
||||
* Return true if this accessible allows accessible children from anonymous subtree.
|
||||
*/
|
||||
virtual bool CanHaveAnonChildren();
|
||||
|
||||
/**
|
||||
* Return true if the accessible is an acceptable child.
|
||||
*/
|
||||
@@ -925,6 +920,12 @@ public:
|
||||
mStateFlags &= ~eRelocated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the accessible doesn't allow accessible children from XBL
|
||||
* anonymous subtree.
|
||||
*/
|
||||
bool NoXBLKids() { return mStateFlags & eNoXBLKids; }
|
||||
|
||||
/**
|
||||
* Return true if this accessible has a parent whose name depends on this
|
||||
* accessible.
|
||||
@@ -1016,8 +1017,9 @@ protected:
|
||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||
eRelocated = 1 << 9, // accessible was moved in tree
|
||||
eNoXBLKids = 1 << 10, // accessible don't allows XBL children
|
||||
|
||||
eLastStateFlag = eRelocated
|
||||
eLastStateFlag = eNoXBLKids
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1132,7 +1134,7 @@ protected:
|
||||
int32_t mIndexInParent;
|
||||
|
||||
static const uint8_t kChildrenFlagsBits = 2;
|
||||
static const uint8_t kStateFlagsBits = 10;
|
||||
static const uint8_t kStateFlagsBits = 11;
|
||||
static const uint8_t kContextFlagsBits = 2;
|
||||
static const uint8_t kTypeBits = 6;
|
||||
static const uint8_t kGenericTypesBits = 14;
|
||||
|
||||
@@ -1437,7 +1437,7 @@ DocAccessible::CacheChildren()
|
||||
// Ignore last HTML:br, copied from HyperTextAccessible.
|
||||
TreeWalker walker(this, rootElm);
|
||||
Accessible* lastChild = nullptr;
|
||||
while (Accessible* child = walker.NextChild()) {
|
||||
while (Accessible* child = walker.Next()) {
|
||||
if (lastChild)
|
||||
AppendChild(lastChild);
|
||||
|
||||
|
||||
@@ -284,7 +284,7 @@ HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
if (container) {
|
||||
TreeWalker walker(container, findNode->AsContent(),
|
||||
TreeWalker::eWalkContextTree);
|
||||
descendant = walker.NextChild();
|
||||
descendant = walker.Next();
|
||||
if (!descendant)
|
||||
descendant = container;
|
||||
}
|
||||
@@ -1945,7 +1945,7 @@ HyperTextAccessible::CacheChildren()
|
||||
TreeWalker walker(this, mContent);
|
||||
Accessible* child = nullptr;
|
||||
Accessible* lastChild = nullptr;
|
||||
while ((child = walker.NextChild())) {
|
||||
while ((child = walker.Next())) {
|
||||
if (lastChild)
|
||||
AppendChild(lastChild);
|
||||
|
||||
|
||||
@@ -403,10 +403,10 @@ HTMLTableAccessible::CacheChildren()
|
||||
TreeWalker walker(this, mContent);
|
||||
|
||||
Accessible* child = nullptr;
|
||||
while ((child = walker.NextChild())) {
|
||||
while ((child = walker.Next())) {
|
||||
if (child->Role() == roles::CAPTION) {
|
||||
InsertChildAt(0, child);
|
||||
while ((child = walker.NextChild()) && AppendChild(child));
|
||||
while ((child = walker.Next()) && AppendChild(child));
|
||||
break;
|
||||
}
|
||||
AppendChild(child);
|
||||
|
||||
@@ -1048,7 +1048,7 @@ DocAccessibleChild::RecvColHeaderCells(const uint64_t& aID,
|
||||
{
|
||||
TableCellAccessible* acc = IdToTableCellAccessible(aID);
|
||||
if (acc) {
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
acc->ColHeaderCells(&headerCells);
|
||||
aCells->SetCapacity(headerCells.Length());
|
||||
for (uint32_t i = 0; i < headerCells.Length(); ++i) {
|
||||
@@ -1066,7 +1066,7 @@ DocAccessibleChild::RecvRowHeaderCells(const uint64_t& aID,
|
||||
{
|
||||
TableCellAccessible* acc = IdToTableCellAccessible(aID);
|
||||
if (acc) {
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
acc->RowHeaderCells(&headerCells);
|
||||
aCells->SetCapacity(headerCells.Length());
|
||||
for (uint32_t i = 0; i < headerCells.Length(); ++i) {
|
||||
@@ -1368,7 +1368,7 @@ DocAccessibleChild::RecvTableSelectedCells(const uint64_t& aID,
|
||||
{
|
||||
TableAccessible* acc = IdToTableAccessible(aID);
|
||||
if (acc) {
|
||||
nsAutoTArray<Accessible*, 30> cells;
|
||||
AutoTArray<Accessible*, 30> cells;
|
||||
acc->SelectedCells(&cells);
|
||||
aCellIDs->SetCapacity(cells.Length());
|
||||
for (uint32_t i = 0; i < cells.Length(); ++i) {
|
||||
@@ -1529,7 +1529,7 @@ DocAccessibleChild::RecvSelectedItems(const uint64_t& aID,
|
||||
{
|
||||
Accessible* acc = IdToAccessibleSelect(aID);
|
||||
if (acc) {
|
||||
nsAutoTArray<Accessible*, 10> selectedItems;
|
||||
AutoTArray<Accessible*, 10> selectedItems;
|
||||
acc->SelectedItems(&selectedItems);
|
||||
aSelectedItemIDs->SetCapacity(selectedItems.Length());
|
||||
for (size_t i = 0; i < selectedItems.Length(); ++i) {
|
||||
|
||||
@@ -267,6 +267,8 @@ DocAccessibleParent::Destroy()
|
||||
ProxyDestroyed(iter.Get()->mProxy);
|
||||
iter.Remove();
|
||||
}
|
||||
|
||||
DocManager::NotifyOfRemoteDocShutdown(this);
|
||||
ProxyDestroyed(this);
|
||||
if (mParentDoc)
|
||||
mParentDoc->RemoveChildDoc(this);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "mozilla/a11y/Platform.h"
|
||||
#include "RelationType.h"
|
||||
#include "mozilla/a11y/Role.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
@@ -23,6 +24,11 @@ ProxyAccessible::Shutdown()
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(!IsDoc());
|
||||
NS_ASSERTION(!mOuterDoc, "Why do we still have a child doc?");
|
||||
xpcAccessibleDocument* xpcDoc =
|
||||
GetAccService()->GetCachedXPCDocument(Document());
|
||||
if (xpcDoc) {
|
||||
xpcDoc->NotifyOfShutdown(this);
|
||||
}
|
||||
|
||||
// XXX Ideally this wouldn't be necessary, but it seems OuterDoc accessibles
|
||||
// can be destroyed before the doc they own.
|
||||
@@ -772,7 +778,7 @@ ProxyAccessible::TableSelectedRowCount()
|
||||
void
|
||||
ProxyAccessible::TableSelectedCells(nsTArray<ProxyAccessible*>* aCellIDs)
|
||||
{
|
||||
nsAutoTArray<uint64_t, 30> cellIDs;
|
||||
AutoTArray<uint64_t, 30> cellIDs;
|
||||
Unused << mDoc->SendTableSelectedCells(mID, &cellIDs);
|
||||
aCellIDs->SetCapacity(cellIDs.Length());
|
||||
for (uint32_t i = 0; i < cellIDs.Length(); ++i) {
|
||||
@@ -851,7 +857,7 @@ ProxyAccessible::AtkTableRowHeader(int32_t aRow)
|
||||
void
|
||||
ProxyAccessible::SelectedItems(nsTArray<ProxyAccessible*>* aSelectedItems)
|
||||
{
|
||||
nsAutoTArray<uint64_t, 10> itemIDs;
|
||||
AutoTArray<uint64_t, 10> itemIDs;
|
||||
Unused << mDoc->SendSelectedItems(mID, &itemIDs);
|
||||
aSelectedItems->SetCapacity(itemIDs.Length());
|
||||
for (size_t i = 0; i < itemIDs.Length(); ++i) {
|
||||
|
||||
@@ -24,6 +24,7 @@ if CONFIG['ACCESSIBILITY']:
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../generic',
|
||||
'../xpcom',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_ENABLE_GTK']:
|
||||
|
||||
@@ -420,7 +420,7 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
||||
nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
|
||||
nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
|
||||
} else {
|
||||
nsAutoTArray<Attribute, 10> attrs;
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->Attributes(&attrs);
|
||||
for (size_t i = 0 ; i < attrs.Length() ; i++) {
|
||||
if (attrs.ElementAt(i).Name() == "thickness") {
|
||||
@@ -684,11 +684,11 @@ ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
||||
|
||||
// get the array of children.
|
||||
if (accWrap) {
|
||||
nsAutoTArray<Accessible*, 10> childrenArray;
|
||||
AutoTArray<Accessible*, 10> childrenArray;
|
||||
accWrap->GetUnignoredChildren(&childrenArray);
|
||||
mChildren = ConvertToNSArray(childrenArray);
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
nsAutoTArray<ProxyAccessible*, 10> childrenArray;
|
||||
AutoTArray<ProxyAccessible*, 10> childrenArray;
|
||||
GetProxyUnignoredChildren(proxy, &childrenArray);
|
||||
mChildren = ConvertToNSArray(childrenArray);
|
||||
}
|
||||
|
||||
@@ -207,12 +207,12 @@
|
||||
return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(),
|
||||
cell->ColExtent())];
|
||||
if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->RowHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->ColHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
|
||||
@@ -768,7 +768,7 @@ ia2Accessible::get_selectionRanges(IA2Range** aRanges,
|
||||
if (acc->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<TextRange, 1> ranges;
|
||||
AutoTArray<TextRange, 1> ranges;
|
||||
acc->Document()->SelectionRanges(&ranges);
|
||||
uint32_t len = ranges.Length();
|
||||
for (uint32_t idx = 0; idx < len; idx++) {
|
||||
|
||||
@@ -371,7 +371,7 @@ ia2AccessibleTable::get_selectedChildren(long aMaxChildren, long** aChildren,
|
||||
if (!mTable)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<uint32_t, 30> cellIndices;
|
||||
AutoTArray<uint32_t, 30> cellIndices;
|
||||
mTable->SelectedCellIndices(&cellIndices);
|
||||
|
||||
uint32_t maxCells = cellIndices.Length();
|
||||
@@ -663,7 +663,7 @@ ia2AccessibleTable::get_selectedCells(IUnknown*** aCells, long* aNSelectedCells)
|
||||
if (!mTable)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<Accessible*, 30> cells;
|
||||
AutoTArray<Accessible*, 30> cells;
|
||||
mTable->SelectedCells(&cells);
|
||||
if (cells.IsEmpty())
|
||||
return S_FALSE;
|
||||
@@ -699,7 +699,7 @@ ia2AccessibleTable::get_selectedColumns(long** aColumns, long* aNColumns)
|
||||
if (!mTable)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<uint32_t, 30> colIndices;
|
||||
AutoTArray<uint32_t, 30> colIndices;
|
||||
mTable->SelectedColIndices(&colIndices);
|
||||
|
||||
uint32_t maxCols = colIndices.Length();
|
||||
@@ -729,7 +729,7 @@ ia2AccessibleTable::get_selectedRows(long** aRows, long* aNRows)
|
||||
if (!mTable)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<uint32_t, 30> rowIndices;
|
||||
AutoTArray<uint32_t, 30> rowIndices;
|
||||
mTable->SelectedRowIndices(&rowIndices);
|
||||
|
||||
uint32_t maxRows = rowIndices.Length();
|
||||
|
||||
@@ -100,7 +100,7 @@ ia2AccessibleTableCell::get_columnHeaderCells(IUnknown*** aCellAccessibles,
|
||||
if (!mTableCell)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<Accessible*, 10> cells;
|
||||
AutoTArray<Accessible*, 10> cells;
|
||||
mTableCell->ColHeaderCells(&cells);
|
||||
|
||||
*aNColumnHeaderCells = cells.Length();
|
||||
@@ -172,7 +172,7 @@ ia2AccessibleTableCell::get_rowHeaderCells(IUnknown*** aCellAccessibles,
|
||||
if (!mTableCell)
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<Accessible*, 10> cells;
|
||||
AutoTArray<Accessible*, 10> cells;
|
||||
mTableCell->RowHeaderCells(&cells);
|
||||
|
||||
*aNRowHeaderCells = cells.Length();
|
||||
|
||||
@@ -61,7 +61,7 @@ ia2AccessibleText::get_attributes(long aOffset, long *aStartOffset,
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
HRESULT hr;
|
||||
if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
|
||||
nsAutoTArray<Attribute, 10> attrs;
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->TextAttributes(true, aOffset, &attrs, &startOffset, &endOffset);
|
||||
hr = AccessibleWrap::ConvertToIA2Attributes(&attrs, aTextAttributes);
|
||||
} else {
|
||||
|
||||
@@ -837,8 +837,18 @@ AccessibleWrap::get_accSelection(VARIANT __RPC_FAR *pvarChildren)
|
||||
return E_NOTIMPL;
|
||||
|
||||
if (IsSelect()) {
|
||||
nsAutoTArray<Accessible*, 10> selectedItems;
|
||||
SelectedItems(&selectedItems);
|
||||
AutoTArray<Accessible*, 10> selectedItems;
|
||||
if (IsProxy()) {
|
||||
nsTArray<ProxyAccessible*> proxies;
|
||||
Proxy()->SelectedItems(&proxies);
|
||||
|
||||
uint32_t selectedCount = proxies.Length();
|
||||
for (uint32_t i = 0; i < selectedCount; i++) {
|
||||
selectedItems.AppendElement(WrapperFor(proxies[i]));
|
||||
}
|
||||
} else {
|
||||
SelectedItems(&selectedItems);
|
||||
}
|
||||
|
||||
// 1) Create and initialize the enumeration
|
||||
RefPtr<AccessibleEnumerator> pEnum = new AccessibleEnumerator(selectedItems);
|
||||
|
||||
@@ -60,3 +60,5 @@ xpc_acc_events_cpp.script = 'AccEventGen.py:gen_cpp_file'
|
||||
xpc_acc_events_cpp.inputs += ['AccEvents.conf']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class AccessibleOrProxy;
|
||||
|
||||
/**
|
||||
* XPCOM nsIAccessible interface implementation, used by xpcAccessibleGeneric
|
||||
@@ -92,6 +93,7 @@ protected:
|
||||
|
||||
private:
|
||||
Accessible* Intl();
|
||||
AccessibleOrProxy IntlGeneric();
|
||||
|
||||
xpcAccessible(const xpcAccessible&) = delete;
|
||||
xpcAccessible& operator =(const xpcAccessible&) = delete;
|
||||
|
||||
@@ -36,7 +36,7 @@ protected:
|
||||
virtual ~xpcAccessibleApplication() {}
|
||||
|
||||
private:
|
||||
ApplicationAccessible* Intl() { return mIntl->AsApplication(); }
|
||||
ApplicationAccessible* Intl() { return mIntl.AsAccessible()->AsApplication(); }
|
||||
|
||||
xpcAccessibleApplication(const xpcAccessibleApplication&) = delete;
|
||||
xpcAccessibleApplication& operator =(const xpcAccessibleApplication&) = delete;
|
||||
|
||||
@@ -9,9 +9,11 @@
|
||||
#include "xpcAccessibleTable.h"
|
||||
#include "xpcAccessibleTableCell.h"
|
||||
|
||||
#include "mozilla/a11y/DocAccessibleParent.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -168,6 +170,7 @@ xpcAccessibleDocument::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor)
|
||||
xpcAccessibleGeneric*
|
||||
xpcAccessibleDocument::GetAccessible(Accessible* aAccessible)
|
||||
{
|
||||
MOZ_ASSERT(!mRemote);
|
||||
if (ToXPCDocument(aAccessible->Document()) != this) {
|
||||
NS_ERROR("This XPCOM document is not related with given internal accessible!");
|
||||
return nullptr;
|
||||
@@ -195,6 +198,27 @@ xpcAccessibleDocument::GetAccessible(Accessible* aAccessible)
|
||||
return xpcAcc;
|
||||
}
|
||||
|
||||
xpcAccessibleGeneric*
|
||||
xpcAccessibleDocument::GetXPCAccessible(ProxyAccessible* aProxy)
|
||||
{
|
||||
MOZ_ASSERT(mRemote);
|
||||
MOZ_ASSERT(aProxy->Document() == mIntl.AsProxy());
|
||||
if (aProxy->IsDoc()) {
|
||||
return this;
|
||||
}
|
||||
|
||||
xpcAccessibleGeneric* acc = mCache.GetWeak(aProxy);
|
||||
if (acc) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
// XXX support exposing optional interfaces.
|
||||
acc = new xpcAccessibleGeneric(aProxy, 0);
|
||||
mCache.Put(aProxy, acc);
|
||||
|
||||
return acc;
|
||||
}
|
||||
|
||||
void
|
||||
xpcAccessibleDocument::Shutdown()
|
||||
{
|
||||
@@ -204,3 +228,18 @@ xpcAccessibleDocument::Shutdown()
|
||||
}
|
||||
xpcAccessibleGeneric::Shutdown();
|
||||
}
|
||||
|
||||
xpcAccessibleGeneric*
|
||||
a11y::ToXPC(AccessibleOrProxy aAcc)
|
||||
{
|
||||
if (aAcc.IsNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aAcc.IsAccessible()) {
|
||||
return ToXPC(aAcc.AsAccessible());
|
||||
}
|
||||
|
||||
xpcAccessibleDocument* doc = ToXPCDocument(aAcc.AsProxy()->Document());
|
||||
return doc->GetXPCAccessible(aAcc.AsProxy());
|
||||
}
|
||||
|
||||
@@ -25,7 +25,11 @@ class xpcAccessibleDocument : public xpcAccessibleHyperText,
|
||||
{
|
||||
public:
|
||||
explicit xpcAccessibleDocument(DocAccessible* aIntl) :
|
||||
xpcAccessibleHyperText(aIntl), mCache(kDefaultCacheLength) { }
|
||||
xpcAccessibleHyperText(aIntl), mCache(kDefaultCacheLength), mRemote(false) { }
|
||||
|
||||
xpcAccessibleDocument(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
xpcAccessibleHyperText(aProxy, aInterfaces), mCache(kDefaultCacheLength),
|
||||
mRemote(true) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(xpcAccessibleDocument,
|
||||
@@ -51,6 +55,7 @@ public:
|
||||
* Return XPCOM wrapper for the internal accessible.
|
||||
*/
|
||||
xpcAccessibleGeneric* GetAccessible(Accessible* aAccessible);
|
||||
xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
@@ -58,10 +63,18 @@ protected:
|
||||
virtual ~xpcAccessibleDocument() {}
|
||||
|
||||
private:
|
||||
DocAccessible* Intl() { return mIntl->AsDoc(); }
|
||||
DocAccessible* Intl()
|
||||
{
|
||||
if (Accessible* acc = mIntl.AsAccessible()) {
|
||||
return acc->AsDoc();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NotifyOfShutdown(Accessible* aAccessible)
|
||||
{
|
||||
MOZ_ASSERT(!mRemote);
|
||||
xpcAccessibleGeneric* xpcAcc = mCache.GetWeak(aAccessible);
|
||||
if (xpcAcc)
|
||||
xpcAcc->Shutdown();
|
||||
@@ -69,13 +82,26 @@ private:
|
||||
mCache.Remove(aAccessible);
|
||||
}
|
||||
|
||||
void NotifyOfShutdown(ProxyAccessible* aProxy)
|
||||
{
|
||||
MOZ_ASSERT(mRemote);
|
||||
xpcAccessibleGeneric* acc = mCache.GetWeak(aProxy);
|
||||
if (acc) {
|
||||
acc->Shutdown();
|
||||
}
|
||||
|
||||
mCache.Remove(aProxy);
|
||||
}
|
||||
|
||||
friend class DocManager;
|
||||
friend class DocAccessible;
|
||||
friend class ProxyAccessible;
|
||||
|
||||
xpcAccessibleDocument(const xpcAccessibleDocument&) = delete;
|
||||
xpcAccessibleDocument& operator =(const xpcAccessibleDocument&) = delete;
|
||||
|
||||
nsRefPtrHashtable<nsPtrHashKey<const Accessible>, xpcAccessibleGeneric> mCache;
|
||||
nsRefPtrHashtable<nsPtrHashKey<const void>, xpcAccessibleGeneric> mCache;
|
||||
bool mRemote;
|
||||
};
|
||||
|
||||
inline xpcAccessibleGeneric*
|
||||
@@ -92,6 +118,8 @@ ToXPC(Accessible* aAccessible)
|
||||
return xpcDoc ? xpcDoc->GetAccessible(aAccessible) : nullptr;
|
||||
}
|
||||
|
||||
xpcAccessibleGeneric* ToXPC(AccessibleOrProxy aAcc);
|
||||
|
||||
inline xpcAccessibleHyperText*
|
||||
ToXPCText(HyperTextAccessible* aAccessible)
|
||||
{
|
||||
@@ -109,6 +137,12 @@ ToXPCDocument(DocAccessible* aAccessible)
|
||||
return GetAccService()->GetXPCDocument(aAccessible);
|
||||
}
|
||||
|
||||
inline xpcAccessibleDocument*
|
||||
ToXPCDocument(DocAccessibleParent* aAccessible)
|
||||
{
|
||||
return GetAccService()->GetXPCDocument(aAccessible);
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessibleGeneric)
|
||||
Accessible*
|
||||
xpcAccessibleGeneric::ToInternalAccessible() const
|
||||
{
|
||||
return mIntl;
|
||||
return mIntl.AsAccessible();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "xpcAccessibleValue.h"
|
||||
|
||||
#include "Accessible.h"
|
||||
#include "AccessibleOrProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
@@ -29,14 +30,28 @@ public:
|
||||
explicit xpcAccessibleGeneric(Accessible* aInternal) :
|
||||
mIntl(aInternal), mSupportedIfaces(0)
|
||||
{
|
||||
if (mIntl->IsSelect())
|
||||
if (aInternal->IsSelect())
|
||||
mSupportedIfaces |= eSelectable;
|
||||
if (mIntl->HasNumericValue())
|
||||
if (aInternal->HasNumericValue())
|
||||
mSupportedIfaces |= eValue;
|
||||
if (mIntl->IsLink())
|
||||
if (aInternal->IsLink())
|
||||
mSupportedIfaces |= eHyperLink;
|
||||
}
|
||||
|
||||
xpcAccessibleGeneric(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
mIntl(aProxy), mSupportedIfaces(0)
|
||||
{
|
||||
if (aInterfaces & Interfaces::SELECTION) {
|
||||
mSupportedIfaces |= eSelectable;
|
||||
}
|
||||
if (aInterfaces & Interfaces::VALUE) {
|
||||
mSupportedIfaces |= eValue;
|
||||
}
|
||||
if (aInterfaces & Interfaces::HYPERLINK) {
|
||||
mSupportedIfaces |= eHyperLink;
|
||||
}
|
||||
}
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(xpcAccessibleGeneric, nsIAccessible)
|
||||
|
||||
@@ -49,7 +64,7 @@ public:
|
||||
protected:
|
||||
virtual ~xpcAccessibleGeneric() {}
|
||||
|
||||
Accessible* mIntl;
|
||||
AccessibleOrProxy mIntl;
|
||||
|
||||
enum {
|
||||
eSelectable = 1 << 0,
|
||||
@@ -72,6 +87,12 @@ private:
|
||||
|
||||
inline Accessible*
|
||||
xpcAccessible::Intl()
|
||||
{
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl.AsAccessible();
|
||||
}
|
||||
|
||||
inline AccessibleOrProxy
|
||||
xpcAccessible::IntlGeneric()
|
||||
{
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl;
|
||||
}
|
||||
@@ -79,19 +100,19 @@ xpcAccessible::Intl()
|
||||
inline Accessible*
|
||||
xpcAccessibleHyperLink::Intl()
|
||||
{
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl;
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl.AsAccessible();
|
||||
}
|
||||
|
||||
inline Accessible*
|
||||
xpcAccessibleSelectable::Intl()
|
||||
{
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl;
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl.AsAccessible();
|
||||
}
|
||||
|
||||
inline Accessible*
|
||||
xpcAccessibleValue::Intl()
|
||||
{
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl;
|
||||
return static_cast<xpcAccessibleGeneric*>(this)->mIntl.AsAccessible();
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
|
||||
@@ -371,7 +371,7 @@ xpcAccessibleHyperText::GetSelectionRanges(nsIArray** aRanges)
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoTArray<TextRange, 1> ranges;
|
||||
AutoTArray<TextRange, 1> ranges;
|
||||
Intl()->SelectionRanges(&ranges);
|
||||
uint32_t len = ranges.Length();
|
||||
for (uint32_t idx = 0; idx < len; idx++)
|
||||
|
||||
@@ -26,10 +26,13 @@ public:
|
||||
explicit xpcAccessibleHyperText(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl)
|
||||
{
|
||||
if (mIntl->IsHyperText() && mIntl->AsHyperText()->IsTextRole())
|
||||
if (aIntl->IsHyperText() && aIntl->AsHyperText()->IsTextRole())
|
||||
mSupportedIfaces |= eText;
|
||||
}
|
||||
|
||||
xpcAccessibleHyperText(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
xpcAccessibleGeneric(aProxy, aInterfaces) { mSupportedIfaces |= eText; }
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_DECL_NSIACCESSIBLETEXT
|
||||
@@ -40,7 +43,14 @@ protected:
|
||||
virtual ~xpcAccessibleHyperText() {}
|
||||
|
||||
private:
|
||||
HyperTextAccessible* Intl() { return mIntl->AsHyperText(); }
|
||||
HyperTextAccessible* Intl()
|
||||
{
|
||||
if (Accessible* acc = mIntl.AsAccessible()) {
|
||||
return acc->AsHyperText();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
xpcAccessibleHyperText(const xpcAccessibleHyperText&) = delete;
|
||||
xpcAccessibleHyperText& operator =(const xpcAccessibleHyperText&) = delete;
|
||||
|
||||
@@ -21,6 +21,9 @@ public:
|
||||
explicit xpcAccessibleImage(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl) { }
|
||||
|
||||
xpcAccessibleImage(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
xpcAccessibleGeneric(aProxy, aInterfaces) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
NS_IMETHOD GetImagePosition(uint32_t aCoordType,
|
||||
@@ -31,7 +34,8 @@ protected:
|
||||
virtual ~xpcAccessibleImage() {}
|
||||
|
||||
private:
|
||||
ImageAccessible* Intl() { return mIntl->AsImage(); }
|
||||
ImageAccessible* Intl()
|
||||
{ return mIntl.IsAccessible() ? mIntl.AsAccessible()->AsImage() : nullptr; }
|
||||
|
||||
xpcAccessibleImage(const xpcAccessibleImage&) = delete;
|
||||
xpcAccessibleImage& operator =(const xpcAccessibleImage&) = delete;
|
||||
|
||||
@@ -21,7 +21,7 @@ xpcAccessibleSelectable::GetSelectedItems(nsIArray** aSelectedItems)
|
||||
return NS_ERROR_FAILURE;
|
||||
NS_PRECONDITION(Intl()->IsSelect(), "Called on non selectable widget!");
|
||||
|
||||
nsAutoTArray<Accessible*, 10> items;
|
||||
AutoTArray<Accessible*, 10> items;
|
||||
Intl()->SelectedItems(&items);
|
||||
|
||||
uint32_t itemCount = items.Length();
|
||||
|
||||
@@ -273,7 +273,7 @@ xpcAccessibleTable::GetSelectedCells(nsIArray** aSelectedCells)
|
||||
do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoTArray<Accessible*, XPC_TABLE_DEFAULT_SIZE> cellsArray;
|
||||
AutoTArray<Accessible*, XPC_TABLE_DEFAULT_SIZE> cellsArray;
|
||||
Intl()->SelectedCells(&cellsArray);
|
||||
|
||||
uint32_t totalCount = cellsArray.Length();
|
||||
@@ -299,7 +299,7 @@ xpcAccessibleTable::GetSelectedCellIndices(uint32_t* aCellsArraySize,
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> cellsArray;
|
||||
AutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> cellsArray;
|
||||
Intl()->SelectedCellIndices(&cellsArray);
|
||||
|
||||
*aCellsArraySize = cellsArray.Length();
|
||||
@@ -324,7 +324,7 @@ xpcAccessibleTable::GetSelectedColumnIndices(uint32_t* aColsArraySize,
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> colsArray;
|
||||
AutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> colsArray;
|
||||
Intl()->SelectedColIndices(&colsArray);
|
||||
|
||||
*aColsArraySize = colsArray.Length();
|
||||
@@ -349,7 +349,7 @@ xpcAccessibleTable::GetSelectedRowIndices(uint32_t* aRowsArraySize,
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> rowsArray;
|
||||
AutoTArray<uint32_t, XPC_TABLE_DEFAULT_SIZE> rowsArray;
|
||||
Intl()->SelectedRowIndices(&rowsArray);
|
||||
|
||||
*aRowsArraySize = rowsArray.Length();
|
||||
|
||||
@@ -23,6 +23,9 @@ public:
|
||||
explicit xpcAccessibleTable(Accessible* aIntl) :
|
||||
xpcAccessibleGeneric(aIntl) { }
|
||||
|
||||
xpcAccessibleTable(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
xpcAccessibleGeneric(aProxy, aInterfaces) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAccessibleTable
|
||||
@@ -80,7 +83,8 @@ protected:
|
||||
virtual ~xpcAccessibleTable() {}
|
||||
|
||||
private:
|
||||
TableAccessible* Intl() { return mIntl->AsTable(); }
|
||||
TableAccessible* Intl()
|
||||
{ return mIntl.IsAccessible() ? mIntl.AsAccessible()->AsTable() : nullptr; }
|
||||
|
||||
xpcAccessibleTable(const xpcAccessibleTable&) = delete;
|
||||
xpcAccessibleTable& operator =(const xpcAccessibleTable&) = delete;
|
||||
|
||||
@@ -108,7 +108,7 @@ xpcAccessibleTableCell::GetColumnHeaderCells(nsIArray** aHeaderCells)
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
Intl()->ColHeaderCells(&headerCells);
|
||||
|
||||
nsCOMPtr<nsIMutableArray> cells = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
@@ -132,7 +132,7 @@ xpcAccessibleTableCell::GetRowHeaderCells(nsIArray** aHeaderCells)
|
||||
if (!Intl())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoTArray<Accessible*, 10> headerCells;
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
Intl()->RowHeaderCells(&headerCells);
|
||||
|
||||
nsCOMPtr<nsIMutableArray> cells = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
|
||||
@@ -24,6 +24,9 @@ public:
|
||||
explicit xpcAccessibleTableCell(Accessible* aIntl) :
|
||||
xpcAccessibleHyperText(aIntl) { }
|
||||
|
||||
xpcAccessibleTableCell(ProxyAccessible* aProxy, uint32_t aInterfaces) :
|
||||
xpcAccessibleHyperText(aProxy, aInterfaces) {}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
// nsIAccessibleTableCell
|
||||
@@ -40,7 +43,14 @@ protected:
|
||||
virtual ~xpcAccessibleTableCell() {}
|
||||
|
||||
private:
|
||||
TableCellAccessible* Intl() { return mIntl->AsTableCell(); }
|
||||
TableCellAccessible* Intl()
|
||||
{
|
||||
if (Accessible* acc = mIntl.AsAccessible()) {
|
||||
return acc->AsTableCell();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
xpcAccessibleTableCell(const xpcAccessibleTableCell&) = delete;
|
||||
xpcAccessibleTableCell& operator =(const xpcAccessibleTableCell&) = delete;
|
||||
|
||||
@@ -31,6 +31,15 @@ XULComboboxAccessible::
|
||||
mGenericTypes |= eAutoComplete;
|
||||
else
|
||||
mGenericTypes |= eCombobox;
|
||||
|
||||
// Both the XUL <textbox type="autocomplete"> and <menulist editable="true">
|
||||
// widgets use XULComboboxAccessible. We need to walk the anonymous children
|
||||
// for these so that the entry field is a child. Otherwise no XBL children.
|
||||
if (!mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) &&
|
||||
!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
|
||||
nsGkAtoms::_true, eIgnoreCase)) {
|
||||
mStateFlags |= eNoXBLKids;
|
||||
}
|
||||
}
|
||||
|
||||
role
|
||||
@@ -96,23 +105,6 @@ XULComboboxAccessible::Value(nsString& aValue)
|
||||
menuList->GetLabel(aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
XULComboboxAccessible::CanHaveAnonChildren()
|
||||
{
|
||||
if (mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) ||
|
||||
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
|
||||
nsGkAtoms::_true, eIgnoreCase)) {
|
||||
// Both the XUL <textbox type="autocomplete"> and <menulist editable="true"> widgets
|
||||
// use XULComboboxAccessible. We need to walk the anonymous children for these
|
||||
// so that the entry field is a child
|
||||
return true;
|
||||
}
|
||||
|
||||
// Argument of false indicates we don't walk anonymous children for
|
||||
// menuitems
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t
|
||||
XULComboboxAccessible::ActionCount()
|
||||
{
|
||||
|
||||
@@ -26,7 +26,6 @@ public:
|
||||
virtual void Value(nsString& aValue) override;
|
||||
virtual a11y::role NativeRole() override;
|
||||
virtual uint64_t NativeState() override;
|
||||
virtual bool CanHaveAnonChildren() override;
|
||||
|
||||
// ActionAccessible
|
||||
virtual uint8_t ActionCount() override;
|
||||
|
||||
@@ -544,6 +544,10 @@ XULListitemAccessible::
|
||||
nsGkAtoms::checkbox,
|
||||
eCaseMatters);
|
||||
mType = eXULListItemType;
|
||||
|
||||
// Walk XBL anonymous children for list items. Overrides the flag value from
|
||||
// base XULMenuitemAccessible class.
|
||||
mStateFlags &= ~eNoXBLKids;
|
||||
}
|
||||
|
||||
XULListitemAccessible::~XULListitemAccessible()
|
||||
@@ -668,13 +672,6 @@ XULListitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
XULListitemAccessible::CanHaveAnonChildren()
|
||||
{
|
||||
// That indicates we should walk anonymous children for listitems
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULListitemAccessible: Widgets
|
||||
|
||||
|
||||
@@ -115,7 +115,6 @@ public:
|
||||
virtual a11y::role NativeRole() override;
|
||||
virtual uint64_t NativeState() override;
|
||||
virtual uint64_t NativeInteractiveState() const override;
|
||||
virtual bool CanHaveAnonChildren() override;
|
||||
|
||||
// Actions
|
||||
virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
|
||||
|
||||
@@ -41,6 +41,7 @@ XULMenuitemAccessible::
|
||||
XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mStateFlags |= eNoXBLKids;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
@@ -267,13 +268,6 @@ XULMenuitemAccessible::GetLevelInternal()
|
||||
return nsAccUtils::GetLevelForXULContainerItem(mContent);
|
||||
}
|
||||
|
||||
bool
|
||||
XULMenuitemAccessible::CanHaveAnonChildren()
|
||||
{
|
||||
// That indicates we don't walk anonymous children for menuitems
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
XULMenuitemAccessible::DoAction(uint8_t index)
|
||||
{
|
||||
|
||||
@@ -30,8 +30,6 @@ public:
|
||||
virtual uint64_t NativeInteractiveState() const override;
|
||||
virtual int32_t GetLevelInternal() override;
|
||||
|
||||
virtual bool CanHaveAnonChildren() override;
|
||||
|
||||
// ActionAccessible
|
||||
virtual uint8_t ActionCount() override;
|
||||
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
|
||||
|
||||
@@ -23,7 +23,7 @@ XULSliderAccessible::
|
||||
XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mStateFlags |= eHasNumericValue;
|
||||
mStateFlags |= eHasNumericValue | eNoXBLKids;
|
||||
}
|
||||
|
||||
// Accessible
|
||||
@@ -127,13 +127,6 @@ XULSliderAccessible::SetCurValue(double aValue)
|
||||
return SetSliderAttr(nsGkAtoms::curpos, aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
XULSliderAccessible::CanHaveAnonChildren()
|
||||
{
|
||||
// Do not allow anonymous xul:slider be accessible.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Utils
|
||||
|
||||
nsIContent*
|
||||
|
||||
@@ -26,7 +26,6 @@ public:
|
||||
virtual a11y::role NativeRole() override;
|
||||
virtual uint64_t NativeInteractiveState() const override;
|
||||
virtual bool NativelyUnavailable() const override;
|
||||
virtual bool CanHaveAnonChildren() override;
|
||||
|
||||
// Value
|
||||
virtual double MaxValue() const override;
|
||||
|
||||
@@ -1115,6 +1115,12 @@ pref("dom.audiochannel.mutedByDefault", true);
|
||||
// requests.
|
||||
pref("dom.bluetooth.app-origin", "app://bluetooth.gaiamobile.org");
|
||||
|
||||
// Enable W3C WebBluetooth API and disable B2G only GATT client API.
|
||||
pref("dom.bluetooth.webbluetooth.enabled", false);
|
||||
|
||||
// Default device name for Presentation API
|
||||
pref("dom.presentation.device.name", "Firefox OS");
|
||||
|
||||
// Enable notification of performance timing
|
||||
pref("dom.performance.enable_notify_performance_timing", true);
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ let gGestureSupport = {
|
||||
* Source array containing any number of elements
|
||||
* @yield Array that is a subset of the input array from full set to empty
|
||||
*/
|
||||
_power: function GS__power(aArray) {
|
||||
_power: function* GS__power(aArray) {
|
||||
// Create a bitmask based on the length of the array
|
||||
let num = 1 << aArray.length;
|
||||
while (--num >= 0) {
|
||||
|
||||
@@ -301,14 +301,13 @@ function initPluginsRow() {
|
||||
}
|
||||
}
|
||||
|
||||
let entries = [{name: item[1], permission: item[0]} for (item of permissionMap)];
|
||||
let entries = Array.from(permissionMap, item => ({ name: item[1], permission: item[0] }));
|
||||
|
||||
entries.sort(function(a, b) {
|
||||
return ((a.name < b.name) ? -1 : (a.name == b.name ? 0 : 1));
|
||||
});
|
||||
|
||||
let permissionEntries = [
|
||||
fillInPluginPermissionTemplate(p.permission, p.name) for (p of entries)
|
||||
];
|
||||
let permissionEntries = entries.map(p => fillInPluginPermissionTemplate(p.name, p.permission));
|
||||
|
||||
let permPluginsRow = document.getElementById("perm-plugins-row");
|
||||
clearPluginPermissionTemplate();
|
||||
|
||||
@@ -93,11 +93,11 @@
|
||||
|
||||
<!-- Passwords -->
|
||||
<groupbox id="passwordsGroup" orient="vertical">
|
||||
<caption label="&passwords.label;"/>
|
||||
<caption label="&logins.label;"/>
|
||||
|
||||
<hbox id="savePasswordsBox">
|
||||
<checkbox id="savePasswords" flex="1"
|
||||
label="&rememberPasswords.label;" accesskey="&rememberPasswords.accesskey;"
|
||||
label="&rememberLogins.label;" accesskey="&rememberLogins.accesskey;"
|
||||
preference="signon.rememberSignons"
|
||||
onsyncfrompreference="return gSecurityPane.readSavePasswords();"/>
|
||||
<button id="passwordExceptions"
|
||||
@@ -123,7 +123,7 @@
|
||||
<hbox id="showPasswordsBox">
|
||||
<spacer flex="1"/>
|
||||
<button id="showPasswords"
|
||||
label="&savedPasswords.label;" accesskey="&savedPasswords.accesskey;"
|
||||
label="&savedLogins.label;" accesskey="&savedLogins.accesskey;"
|
||||
oncommand="gSecurityPane.showPasswords();"
|
||||
preference="pref.privacy.disable_button.view_passwords"/>
|
||||
</hbox>
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
<!ENTITY addonSecurityLevel_High "Medium: Block all harmful add-ons (default, recommended)">
|
||||
<!ENTITY addonSecurityLevel_Extreme "High: Block all add-ons with known issues">
|
||||
|
||||
<!ENTITY passwords.label "Passwords">
|
||||
<!ENTITY logins.label "Logins">
|
||||
|
||||
<!ENTITY rememberPasswords.label "Remember passwords for sites">
|
||||
<!ENTITY rememberPasswords.accesskey "R">
|
||||
<!ENTITY rememberLogins.label "Remember logins for sites">
|
||||
<!ENTITY rememberLogins.accesskey "R">
|
||||
<!ENTITY passwordExceptions.label "Exceptions…">
|
||||
<!ENTITY passwordExceptions.accesskey "x">
|
||||
|
||||
@@ -31,8 +31,8 @@
|
||||
<!ENTITY changeMasterPassword.label "Change Master Password…">
|
||||
<!ENTITY changeMasterPassword.accesskey "M">
|
||||
|
||||
<!ENTITY savedPasswords.label "Saved Passwords…">
|
||||
<!ENTITY savedPasswords.accesskey "P">
|
||||
<!ENTITY savedLogins.label "Saved Logins…">
|
||||
<!ENTITY savedLogins.accesskey "L">
|
||||
|
||||
<!ENTITY SecProto.label "Security Protocols">
|
||||
<!ENTITY enableHSTS.label "Enable Strict Transport Security (HSTS)">
|
||||
|
||||
@@ -1311,6 +1311,7 @@ QtSparql/qsparqlresult.h
|
||||
pixman.h
|
||||
#endif
|
||||
#if MOZ_NATIVE_LIBVPX==1
|
||||
vpx/svc_context.h
|
||||
vpx/vpx_codec.h
|
||||
vpx/vpx_decoder.h
|
||||
vpx/vpx_encoder.h
|
||||
|
||||
@@ -1699,7 +1699,7 @@ nsDocShell::FirePageHideNotification(bool aIsUnload)
|
||||
mTiming->NotifyUnloadEventEnd();
|
||||
}
|
||||
|
||||
nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
|
||||
AutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
|
||||
uint32_t n = mChildList.Length();
|
||||
kids.SetCapacity(n);
|
||||
for (uint32_t i = 0; i < n; i++) {
|
||||
@@ -4377,7 +4377,7 @@ nsDocShell::RemoveFromSessionHistory()
|
||||
|
||||
int32_t index = 0;
|
||||
sessionHistory->GetIndex(&index);
|
||||
nsAutoTArray<uint64_t, 16> ids;
|
||||
AutoTArray<uint64_t, 16> ids;
|
||||
ids.AppendElement(mHistoryID);
|
||||
internalHistory->RemoveEntries(ids, index);
|
||||
return NS_OK;
|
||||
@@ -4468,7 +4468,7 @@ nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
|
||||
|
||||
int32_t count = 0;
|
||||
shcontainer->GetChildCount(&count);
|
||||
nsAutoTArray<uint64_t, 16> ids;
|
||||
AutoTArray<uint64_t, 16> ids;
|
||||
for (int32_t i = 0; i < count; ++i) {
|
||||
nsCOMPtr<nsISHEntry> child;
|
||||
shcontainer->GetChildAt(i, getter_AddRefs(child));
|
||||
|
||||
@@ -1404,7 +1404,7 @@ nsSHistory::RemoveDynEntries(int32_t aOldIndex, int32_t aNewIndex)
|
||||
nsCOMPtr<nsISHEntry> originalSH;
|
||||
GetEntryAtIndex(aOldIndex, false, getter_AddRefs(originalSH));
|
||||
nsCOMPtr<nsISHContainer> originalContainer = do_QueryInterface(originalSH);
|
||||
nsAutoTArray<uint64_t, 16> toBeRemovedEntries;
|
||||
AutoTArray<uint64_t, 16> toBeRemovedEntries;
|
||||
if (originalContainer) {
|
||||
nsTArray<uint64_t> originalDynDocShellIDs;
|
||||
GetDynamicChildren(originalContainer, originalDynDocShellIDs, true);
|
||||
|
||||
@@ -81,6 +81,41 @@ namespace {
|
||||
// Animation interface:
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
/* static */ already_AddRefed<Animation>
|
||||
Animation::Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationTimeline* aTimeline,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
RefPtr<Animation> animation = new Animation(global);
|
||||
|
||||
if (!aTimeline) {
|
||||
// Bug 1096776: We do not support null timeline yet.
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
if (!aEffect) {
|
||||
// Bug 1049975: We do not support null effect yet.
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
animation->SetTimeline(aTimeline);
|
||||
animation->SetEffect(aEffect);
|
||||
|
||||
return animation.forget();
|
||||
}
|
||||
|
||||
void
|
||||
Animation::SetId(const nsAString& aId)
|
||||
{
|
||||
if (mId == aId) {
|
||||
return;
|
||||
}
|
||||
mId = aId;
|
||||
nsNodeUtils::AnimationChanged(this);
|
||||
}
|
||||
|
||||
void
|
||||
Animation::SetEffect(KeyframeEffectReadOnly* aEffect)
|
||||
@@ -1132,7 +1167,7 @@ Animation::EffectEnd() const
|
||||
return StickyTimeDuration(0);
|
||||
}
|
||||
|
||||
return mEffect->Timing().mDelay
|
||||
return mEffect->SpecifiedTiming().mDelay
|
||||
+ mEffect->GetComputedTiming().mActiveDuration;
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,13 @@ public:
|
||||
};
|
||||
|
||||
// Animation interface methods
|
||||
|
||||
static already_AddRefed<Animation>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
KeyframeEffectReadOnly* aEffect,
|
||||
AnimationTimeline* aTimeline,
|
||||
ErrorResult& aRv);
|
||||
void GetId(nsAString& aResult) const { aResult = mId; }
|
||||
void SetId(const nsAString& aId);
|
||||
KeyframeEffectReadOnly* GetEffect() const { return mEffect; }
|
||||
void SetEffect(KeyframeEffectReadOnly* aEffect);
|
||||
AnimationTimeline* GetTimeline() const { return mTimeline; }
|
||||
@@ -422,6 +428,8 @@ protected:
|
||||
// in that case mFinished is immediately reset to represent a new current
|
||||
// finished promise.
|
||||
bool mFinishedIsResolved;
|
||||
|
||||
nsString mId;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AnimationEffectTimingReadOnly;
|
||||
struct ComputedTimingProperties;
|
||||
|
||||
class AnimationEffectReadOnly : public nsISupports,
|
||||
@@ -30,9 +31,9 @@ public:
|
||||
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
|
||||
virtual void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
|
||||
{
|
||||
}
|
||||
virtual already_AddRefed<AnimationEffectTimingReadOnly> Timing() const = 0;
|
||||
|
||||
virtual void GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const = 0;
|
||||
|
||||
protected:
|
||||
virtual ~AnimationEffectReadOnly() = default;
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
|
||||
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
#include "mozilla/dom/AnimatableBinding.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnlyBinding.h"
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
TimingParams::TimingParams(const dom::AnimationEffectTimingProperties& aRhs,
|
||||
const dom::Element* aTarget)
|
||||
: mDuration(aRhs.mDuration)
|
||||
, mDelay(TimeDuration::FromMilliseconds(aRhs.mDelay))
|
||||
, mIterations(aRhs.mIterations)
|
||||
, mDirection(aRhs.mDirection)
|
||||
, mFill(aRhs.mFill)
|
||||
{
|
||||
mFunction = AnimationUtils::ParseEasing(aTarget, aRhs.mEasing);
|
||||
}
|
||||
|
||||
TimingParams::TimingParams(double aDuration)
|
||||
{
|
||||
mDuration.SetAsUnrestrictedDouble() = aDuration;
|
||||
}
|
||||
|
||||
template <class OptionsType>
|
||||
static const dom::AnimationEffectTimingProperties&
|
||||
GetTimingProperties(const OptionsType& aOptions);
|
||||
|
||||
template <>
|
||||
/* static */ const dom::AnimationEffectTimingProperties&
|
||||
GetTimingProperties(
|
||||
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
|
||||
return aOptions.GetAsKeyframeEffectOptions();
|
||||
}
|
||||
|
||||
template <>
|
||||
/* static */ const dom::AnimationEffectTimingProperties&
|
||||
GetTimingProperties(
|
||||
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions)
|
||||
{
|
||||
MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
|
||||
return aOptions.GetAsKeyframeAnimationOptions();
|
||||
}
|
||||
|
||||
template <class OptionsType>
|
||||
static TimingParams
|
||||
TimingParamsFromOptionsUnion(
|
||||
const OptionsType& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
{
|
||||
if (aOptions.IsUnrestrictedDouble()) {
|
||||
return TimingParams(aOptions.GetAsUnrestrictedDouble());
|
||||
} else {
|
||||
// If aTarget is a pseudo element, we pass its parent element because
|
||||
// TimingParams only needs its owner doc to parse easing and both pseudo
|
||||
// element and its parent element should have the same owner doc.
|
||||
// Bug 1246320: Avoid passing the element for parsing the timing function
|
||||
RefPtr<dom::Element> targetElement;
|
||||
if (!aTarget.IsNull()) {
|
||||
const dom::ElementOrCSSPseudoElement& target = aTarget.Value();
|
||||
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
|
||||
"Uninitialized target");
|
||||
if (target.IsElement()) {
|
||||
targetElement = &target.GetAsElement();
|
||||
} else {
|
||||
targetElement = target.GetAsCSSPseudoElement().ParentElement();
|
||||
}
|
||||
}
|
||||
return TimingParams(GetTimingProperties(aOptions), targetElement);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ TimingParams
|
||||
TimingParams::FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
{
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget);
|
||||
}
|
||||
|
||||
/* static */ TimingParams
|
||||
TimingParams::FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
|
||||
{
|
||||
return TimingParamsFromOptionsUnion(aOptions, aTarget);
|
||||
}
|
||||
|
||||
bool
|
||||
TimingParams::operator==(const TimingParams& aOther) const
|
||||
{
|
||||
bool durationEqual;
|
||||
if (mDuration.IsUnrestrictedDouble()) {
|
||||
durationEqual = aOther.mDuration.IsUnrestrictedDouble() &&
|
||||
(mDuration.GetAsUnrestrictedDouble() ==
|
||||
aOther.mDuration.GetAsUnrestrictedDouble());
|
||||
} else {
|
||||
// We consider all string values and uninitialized values as meaning "auto".
|
||||
// Since mDuration is either a string or uninitialized, we consider it equal
|
||||
// if aOther.mDuration is also either a string or uninitialized.
|
||||
durationEqual = !aOther.mDuration.IsUnrestrictedDouble();
|
||||
}
|
||||
return durationEqual &&
|
||||
mDelay == aOther.mDelay &&
|
||||
mIterations == aOther.mIterations &&
|
||||
mDirection == aOther.mDirection &&
|
||||
mFill == aOther.mFill &&
|
||||
mFunction == aOther.mFunction;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectTimingReadOnly, mParent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationEffectTimingReadOnly, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationEffectTimingReadOnly, Release)
|
||||
|
||||
JSObject*
|
||||
AnimationEffectTimingReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return AnimationEffectTimingReadOnlyBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
AnimationEffectTimingReadOnly::GetEasing(nsString& aRetVal) const
|
||||
{
|
||||
if (mTiming.mFunction.isSome()) {
|
||||
mTiming.mFunction->AppendToString(aRetVal);
|
||||
} else {
|
||||
aRetVal.AssignLiteral("linear");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,108 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_AnimationEffectTimingReadOnly_h
|
||||
#define mozilla_dom_AnimationEffectTimingReadOnly_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
// X11 has a #define for None
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode
|
||||
// and PlaybackDirection
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
struct AnimationEffectTimingProperties;
|
||||
class Element;
|
||||
class UnrestrictedDoubleOrKeyframeEffectOptions;
|
||||
class UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
class ElementOrCSSPseudoElement;
|
||||
}
|
||||
|
||||
struct TimingParams
|
||||
{
|
||||
TimingParams() = default;
|
||||
TimingParams(const dom::AnimationEffectTimingProperties& aTimingProperties,
|
||||
const dom::Element* aTarget);
|
||||
explicit TimingParams(double aDuration);
|
||||
|
||||
static TimingParams FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
|
||||
static TimingParams FromOptionsUnion(
|
||||
const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
|
||||
|
||||
// The unitialized state of mDuration represents "auto".
|
||||
// Bug 1237173: We will replace this with Maybe<TimeDuration>.
|
||||
dom::OwningUnrestrictedDoubleOrString mDuration;
|
||||
TimeDuration mDelay; // Initializes to zero
|
||||
double mIterations = 1.0; // Can be NaN, negative, +/-Infinity
|
||||
dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal;
|
||||
dom::FillMode mFill = dom::FillMode::Auto;
|
||||
Maybe<ComputedTimingFunction> mFunction;
|
||||
|
||||
bool operator==(const TimingParams& aOther) const;
|
||||
bool operator!=(const TimingParams& aOther) const
|
||||
{
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AnimationEffectTimingReadOnly : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
AnimationEffectTimingReadOnly() = default;
|
||||
explicit AnimationEffectTimingReadOnly(const TimingParams& aTiming)
|
||||
: mTiming(aTiming) { }
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEffectTimingReadOnly)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationEffectTimingReadOnly)
|
||||
|
||||
protected:
|
||||
virtual ~AnimationEffectTimingReadOnly() = default;
|
||||
|
||||
public:
|
||||
nsISupports* GetParentObject() const { return mParent; }
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
double Delay() const { return mTiming.mDelay.ToMilliseconds(); }
|
||||
double EndDelay() const { return 0.0; }
|
||||
FillMode Fill() const { return mTiming.mFill; }
|
||||
double IterationStart() const { return 0.0; }
|
||||
double Iterations() const { return mTiming.mIterations; }
|
||||
void GetDuration(OwningUnrestrictedDoubleOrString& aRetVal) const
|
||||
{
|
||||
aRetVal = mTiming.mDuration;
|
||||
}
|
||||
PlaybackDirection Direction() const { return mTiming.mDirection; }
|
||||
void GetEasing(nsString& aRetVal) const;
|
||||
|
||||
const TimingParams& AsTimingParams() const { return mTiming; }
|
||||
void SetTimingParams(const TimingParams& aTiming) { mTiming = aTiming; }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
TimingParams mTiming;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AnimationEffectTimingReadOnly_h
|
||||
@@ -34,48 +34,6 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationTimeline)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
void
|
||||
AnimationTimeline::GetAnimations(AnimationSequence& aAnimations)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mWindow);
|
||||
if (mWindow) {
|
||||
nsIDocument* doc = window->GetDoc();
|
||||
if (doc) {
|
||||
doc->FlushPendingNotifications(Flush_Style);
|
||||
}
|
||||
}
|
||||
|
||||
aAnimations.SetCapacity(mAnimations.Count());
|
||||
|
||||
for (Animation* animation = mAnimationOrder.getFirst(); animation;
|
||||
animation = animation->getNext()) {
|
||||
|
||||
// Skip animations which are no longer relevant or which have been
|
||||
// associated with another timeline. These animations will be removed
|
||||
// on the next tick.
|
||||
if (!animation->IsRelevant() || animation->GetTimeline() != this) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Bug 1174575: Until we implement a suitable PseudoElement interface we
|
||||
// don't have anything to return for the |target| attribute of
|
||||
// KeyframeEffect(ReadOnly) objects that refer to pseudo-elements.
|
||||
// Rather than return some half-baked version of these objects (e.g.
|
||||
// we a null effect attribute) we simply don't provide access to animations
|
||||
// whose effect refers to a pseudo-element until we can support them
|
||||
// properly.
|
||||
Element* target;
|
||||
nsCSSPseudoElements::Type pseudoType;
|
||||
animation->GetEffect()->GetTarget(target, pseudoType);
|
||||
if (pseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
|
||||
aAnimations.AppendElement(animation);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort animations by priority
|
||||
aAnimations.Sort(AnimationPtrComparator<RefPtr<Animation>>());
|
||||
}
|
||||
|
||||
void
|
||||
AnimationTimeline::NotifyAnimationUpdated(Animation& aAnimation)
|
||||
{
|
||||
|
||||
@@ -51,11 +51,8 @@ public:
|
||||
|
||||
nsIGlobalObject* GetParentObject() const { return mWindow; }
|
||||
|
||||
typedef nsTArray<RefPtr<Animation>> AnimationSequence;
|
||||
|
||||
// AnimationTimeline methods
|
||||
virtual Nullable<TimeDuration> GetCurrentTime() const = 0;
|
||||
void GetAnimations(AnimationSequence& aAnimations);
|
||||
|
||||
// Wrapper functions for AnimationTimeline DOM methods when called from
|
||||
// script.
|
||||
|
||||
@@ -6,10 +6,13 @@
|
||||
|
||||
#include "AnimationUtils.h"
|
||||
|
||||
#include "nsCSSParser.h" // For nsCSSParser
|
||||
#include "nsDebug.h"
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
|
||||
#include "mozilla/dom/Element.h" // For dom::Element
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -33,4 +36,66 @@ AnimationUtils::LogAsyncAnimationFailure(nsCString& aMessage,
|
||||
printf_stderr("%s", aMessage.get());
|
||||
}
|
||||
|
||||
/* static */ Maybe<ComputedTimingFunction>
|
||||
AnimationUtils::ParseEasing(const dom::Element* aTarget,
|
||||
const nsAString& aEasing)
|
||||
{
|
||||
if (!aTarget) {
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
nsIDocument* doc = aTarget->OwnerDoc();
|
||||
|
||||
nsCSSValue value;
|
||||
nsCSSParser parser;
|
||||
parser.ParseLonghandProperty(eCSSProperty_animation_timing_function,
|
||||
aEasing,
|
||||
doc->GetDocumentURI(),
|
||||
doc->GetDocumentURI(),
|
||||
doc->NodePrincipal(),
|
||||
value);
|
||||
|
||||
switch (value.GetUnit()) {
|
||||
case eCSSUnit_List: {
|
||||
const nsCSSValueList* list = value.GetListValue();
|
||||
if (list->mNext) {
|
||||
// don't support a list of timing functions
|
||||
break;
|
||||
}
|
||||
switch (list->mValue.GetUnit()) {
|
||||
case eCSSUnit_Enumerated:
|
||||
// Return Nothing() if "linear" is passed in.
|
||||
if (list->mValue.GetIntValue() ==
|
||||
NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) {
|
||||
return Nothing();
|
||||
}
|
||||
// Fall through
|
||||
case eCSSUnit_Cubic_Bezier:
|
||||
case eCSSUnit_Steps: {
|
||||
nsTimingFunction timingFunction;
|
||||
nsRuleNode::ComputeTimingFunction(list->mValue, timingFunction);
|
||||
ComputedTimingFunction computedTimingFunction;
|
||||
computedTimingFunction.Init(timingFunction);
|
||||
return Some(computedTimingFunction);
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function list "
|
||||
"item unit");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eCSSUnit_Null:
|
||||
case eCSSUnit_Inherit:
|
||||
case eCSSUnit_Initial:
|
||||
case eCSSUnit_Unset:
|
||||
case eCSSUnit_TokenStream:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
|
||||
break;
|
||||
}
|
||||
return Nothing();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -15,6 +15,12 @@ class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ComputedTimingFunction;
|
||||
|
||||
namespace dom {
|
||||
class Element;
|
||||
}
|
||||
|
||||
class AnimationUtils
|
||||
{
|
||||
public:
|
||||
@@ -44,6 +50,14 @@ public:
|
||||
|
||||
static void LogAsyncAnimationFailure(nsCString& aMessage,
|
||||
const nsIContent* aContent = nullptr);
|
||||
|
||||
/**
|
||||
* Parses a CSS <single-transition-timing-function> value from
|
||||
* aEasing into a ComputedTimingFunction. If parsing fails, Nothing() will
|
||||
* be returned.
|
||||
*/
|
||||
static Maybe<ComputedTimingFunction>
|
||||
ParseEasing(const dom::Element* aTarget, const nsAString& aEasing);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "mozilla/dom/CSSPseudoElement.h"
|
||||
#include "mozilla/dom/CSSPseudoElementBinding.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CSSPseudoElement, mParentElement)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CSSPseudoElement, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CSSPseudoElement, Release)
|
||||
|
||||
CSSPseudoElement::CSSPseudoElement(Element* aElement,
|
||||
nsCSSPseudoElements::Type aType)
|
||||
: mParentElement(aElement)
|
||||
, mPseudoType(aType)
|
||||
{
|
||||
MOZ_ASSERT(aElement);
|
||||
MOZ_ASSERT(aType == nsCSSPseudoElements::ePseudo_after ||
|
||||
aType == nsCSSPseudoElements::ePseudo_before,
|
||||
"Unexpected Pseudo Type");
|
||||
}
|
||||
|
||||
CSSPseudoElement::~CSSPseudoElement()
|
||||
{
|
||||
// Element might have been unlinked already, so we have to do null check.
|
||||
if (mParentElement) {
|
||||
mParentElement->DeleteProperty(
|
||||
GetCSSPseudoElementPropertyAtom(mPseudoType));
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
CSSPseudoElement::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return CSSPseudoElementBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
CSSPseudoElement::GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal)
|
||||
{
|
||||
// Bug 1234403: Implement this API.
|
||||
NS_NOTREACHED("CSSPseudoElement::GetAnimations() is not implemented yet.");
|
||||
}
|
||||
|
||||
already_AddRefed<Animation>
|
||||
CSSPseudoElement::Animate(
|
||||
JSContext* aContext,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError)
|
||||
{
|
||||
// Bug 1241784: Implement this API.
|
||||
NS_NOTREACHED("CSSPseudoElement::Animate() is not implemented yet.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<CSSPseudoElement>
|
||||
CSSPseudoElement::GetCSSPseudoElement(Element* aElement,
|
||||
nsCSSPseudoElements::Type aType)
|
||||
{
|
||||
if (!aElement) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIAtom* propName = CSSPseudoElement::GetCSSPseudoElementPropertyAtom(aType);
|
||||
RefPtr<CSSPseudoElement> pseudo =
|
||||
static_cast<CSSPseudoElement*>(aElement->GetProperty(propName));
|
||||
if (pseudo) {
|
||||
return pseudo.forget();
|
||||
}
|
||||
|
||||
// CSSPseudoElement is a purely external interface created on-demand, and
|
||||
// when all references from script to the pseudo are dropped, we can drop the
|
||||
// CSSPseudoElement object, so use a non-owning reference from Element to
|
||||
// CSSPseudoElement.
|
||||
pseudo = new CSSPseudoElement(aElement, aType);
|
||||
nsresult rv = aElement->SetProperty(propName, pseudo, nullptr, true);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("SetProperty failed");
|
||||
return nullptr;
|
||||
}
|
||||
return pseudo.forget();
|
||||
}
|
||||
|
||||
/* static */ nsIAtom*
|
||||
CSSPseudoElement::GetCSSPseudoElementPropertyAtom(
|
||||
nsCSSPseudoElements::Type aType)
|
||||
{
|
||||
switch (aType) {
|
||||
case nsCSSPseudoElements::ePseudo_before:
|
||||
return nsGkAtoms::cssPseudoElementBeforeProperty;
|
||||
|
||||
case nsCSSPseudoElements::ePseudo_after:
|
||||
return nsGkAtoms::cssPseudoElementAfterProperty;
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("Should not try to get CSSPseudoElement "
|
||||
"other than ::before or ::after");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
@@ -0,0 +1,88 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_CSSPseudoElement_h
|
||||
#define mozilla_dom_CSSPseudoElement_h
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsCSSPseudoElements.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Animation;
|
||||
class Element;
|
||||
class UnrestrictedDoubleOrKeyframeAnimationOptions;
|
||||
|
||||
class CSSPseudoElement final : public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(CSSPseudoElement)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(CSSPseudoElement)
|
||||
|
||||
protected:
|
||||
virtual ~CSSPseudoElement();
|
||||
|
||||
public:
|
||||
ParentObject GetParentObject() const
|
||||
{
|
||||
return mParentElement->GetParentObject();
|
||||
}
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsCSSPseudoElements::Type GetType() const { return mPseudoType; }
|
||||
void GetType(nsString& aRetVal) const
|
||||
{
|
||||
MOZ_ASSERT(nsCSSPseudoElements::GetPseudoAtom(mPseudoType),
|
||||
"All pseudo-types allowed by this class should have a"
|
||||
" corresponding atom");
|
||||
nsCSSPseudoElements::GetPseudoAtom(mPseudoType)->ToString(aRetVal);
|
||||
}
|
||||
already_AddRefed<Element> ParentElement() const
|
||||
{
|
||||
RefPtr<Element> retVal(mParentElement);
|
||||
return retVal.forget();
|
||||
}
|
||||
|
||||
void GetAnimations(nsTArray<RefPtr<Animation>>& aRetVal);
|
||||
already_AddRefed<Animation>
|
||||
Animate(JSContext* aContext,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
|
||||
ErrorResult& aError);
|
||||
|
||||
// Given an element:pseudoType pair, returns the CSSPseudoElement stored as a
|
||||
// property on |aElement|. If there is no CSSPseudoElement for the specified
|
||||
// pseudo-type on element, a new CSSPseudoElement will be created and stored
|
||||
// on the element.
|
||||
static already_AddRefed<CSSPseudoElement>
|
||||
GetCSSPseudoElement(Element* aElement, nsCSSPseudoElements::Type aType);
|
||||
|
||||
private:
|
||||
// Only ::before and ::after are supported.
|
||||
CSSPseudoElement(Element* aElement, nsCSSPseudoElements::Type aType);
|
||||
|
||||
static nsIAtom*
|
||||
GetCSSPseudoElementPropertyAtom(nsCSSPseudoElements::Type aType);
|
||||
|
||||
// mParentElement needs to be an owning reference since if script is holding
|
||||
// on to the pseudo-element, it needs to continue to be able to refer to
|
||||
// the parent element.
|
||||
RefPtr<Element> mParentElement;
|
||||
nsCSSPseudoElements::Type mPseudoType;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_CSSPseudoElement_h
|
||||
@@ -5,6 +5,7 @@
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ComputedTimingFunction.h"
|
||||
#include "nsAlgorithm.h" // For clamped()
|
||||
#include "nsStyleUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
@@ -34,8 +35,55 @@ double
|
||||
ComputedTimingFunction::GetValue(double aPortion) const
|
||||
{
|
||||
if (HasSpline()) {
|
||||
// Check for a linear curve.
|
||||
// (GetSplineValue(), below, also checks this but doesn't work when
|
||||
// aPortion is outside the range [0.0, 1.0]).
|
||||
if (mTimingFunction.X1() == mTimingFunction.Y1() &&
|
||||
mTimingFunction.X2() == mTimingFunction.Y2()) {
|
||||
return aPortion;
|
||||
}
|
||||
|
||||
// For negative values, try to extrapolate with tangent (p1 - p0) or,
|
||||
// if p1 is coincident with p0, with (p2 - p0).
|
||||
if (aPortion < 0.0) {
|
||||
if (mTimingFunction.X1() > 0.0) {
|
||||
return aPortion * mTimingFunction.Y1() / mTimingFunction.X1();
|
||||
} else if (mTimingFunction.Y1() == 0 && mTimingFunction.X2() > 0.0) {
|
||||
return aPortion * mTimingFunction.Y2() / mTimingFunction.X2();
|
||||
}
|
||||
// If we can't calculate a sensible tangent, don't extrapolate at all.
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// For values greater than 1, try to extrapolate with tangent (p2 - p3) or,
|
||||
// if p2 is coincident with p3, with (p1 - p3).
|
||||
if (aPortion > 1.0) {
|
||||
if (mTimingFunction.X2() < 1.0) {
|
||||
return 1.0 + (aPortion - 1.0) *
|
||||
(mTimingFunction.Y2() - 1) / (mTimingFunction.X2() - 1);
|
||||
} else if (mTimingFunction.Y2() == 1 && mTimingFunction.X1() < 1.0) {
|
||||
return 1.0 + (aPortion - 1.0) *
|
||||
(mTimingFunction.Y1() - 1) / (mTimingFunction.X1() - 1);
|
||||
}
|
||||
// If we can't calculate a sensible tangent, don't extrapolate at all.
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return mTimingFunction.GetSplineValue(aPortion);
|
||||
}
|
||||
|
||||
// Since we use endpoint-exclusive timing, the output of a steps(start) timing
|
||||
// function when aPortion = 0.0 is the top of the first step. When aPortion is
|
||||
// negative, however, we should use the bottom of the first step. We handle
|
||||
// negative values of aPortion specially here since once we clamp aPortion
|
||||
// to [0,1] below we will no longer be able to distinguish to the two cases.
|
||||
if (aPortion < 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Clamp in case of steps(end) and steps(start) for values greater than 1.
|
||||
aPortion = clamped(aPortion, 0.0, 1.0);
|
||||
|
||||
if (mType == nsTimingFunction::Type::StepStart) {
|
||||
// There are diagrams in the spec that seem to suggest this check
|
||||
// and the bounds point should not be symmetric with StepEnd, but
|
||||
@@ -96,4 +144,29 @@ ComputedTimingFunction::AppendToString(nsAString& aResult) const
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ int32_t
|
||||
ComputedTimingFunction::Compare(const Maybe<ComputedTimingFunction>& aLhs,
|
||||
const Maybe<ComputedTimingFunction>& aRhs)
|
||||
{
|
||||
// We can't use |operator<| for const Maybe<>& here because
|
||||
// 'ease' is prior to 'linear' which is represented by Nothing().
|
||||
// So we have to convert Nothing() as 'linear' and check it first.
|
||||
nsTimingFunction::Type lhsType = aLhs.isNothing() ?
|
||||
nsTimingFunction::Type::Linear : aLhs->GetType();
|
||||
nsTimingFunction::Type rhsType = aRhs.isNothing() ?
|
||||
nsTimingFunction::Type::Linear : aRhs->GetType();
|
||||
|
||||
if (lhsType != rhsType) {
|
||||
return int32_t(lhsType) - int32_t(rhsType);
|
||||
}
|
||||
|
||||
// Both of them are Nothing().
|
||||
if (lhsType == nsTimingFunction::Type::Linear) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Other types.
|
||||
return aLhs->Compare(aRhs.value());
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -41,6 +41,14 @@ public:
|
||||
int32_t Compare(const ComputedTimingFunction& aRhs) const;
|
||||
void AppendToString(nsAString& aResult) const;
|
||||
|
||||
static double GetPortion(const Maybe<ComputedTimingFunction>& aFunction,
|
||||
double aPortion)
|
||||
{
|
||||
return aFunction.isSome() ? aFunction->GetValue(aPortion) : aPortion;
|
||||
}
|
||||
static int32_t Compare(const Maybe<ComputedTimingFunction>& aLhs,
|
||||
const Maybe<ComputedTimingFunction>& aRhs);
|
||||
|
||||
private:
|
||||
nsTimingFunction::Type mType;
|
||||
nsSMILKeySpline mTimingFunction;
|
||||
@@ -50,4 +58,4 @@ private:
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_AnimationEffectReadOnly_h
|
||||
#endif // mozilla_ComputedTimingFunction_h
|
||||
|
||||
@@ -551,7 +551,7 @@ EffectCompositor::GetOverriddenProperties(nsStyleContext* aStyleContext,
|
||||
nsCSSPropertySet&
|
||||
aPropertiesOverridden)
|
||||
{
|
||||
nsAutoTArray<nsCSSProperty, LayerAnimationInfo::kRecords> propertiesToTrack;
|
||||
AutoTArray<nsCSSProperty, LayerAnimationInfo::kRecords> propertiesToTrack;
|
||||
{
|
||||
nsCSSPropertySet propertiesToTrackAsSet;
|
||||
for (KeyframeEffectReadOnly* effect : aEffectSet) {
|
||||
|
||||
+162
-213
@@ -6,7 +6,6 @@
|
||||
|
||||
#include "mozilla/dom/KeyframeEffect.h"
|
||||
|
||||
#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"
|
||||
#include "mozilla/dom/KeyframeEffectBinding.h"
|
||||
#include "mozilla/dom/PropertyIndexedKeyframesBinding.h"
|
||||
#include "mozilla/AnimationUtils.h"
|
||||
@@ -24,32 +23,19 @@
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
bool
|
||||
AnimationTiming::FillsForwards() const
|
||||
{
|
||||
return mFillMode == dom::FillMode::Both ||
|
||||
mFillMode == dom::FillMode::Forwards;
|
||||
}
|
||||
|
||||
bool
|
||||
AnimationTiming::FillsBackwards() const
|
||||
{
|
||||
return mFillMode == dom::FillMode::Both ||
|
||||
mFillMode == dom::FillMode::Backwards;
|
||||
}
|
||||
|
||||
// Helper functions for generating a ComputedTimingProperties dictionary
|
||||
static void
|
||||
GetComputedTimingDictionary(const ComputedTiming& aComputedTiming,
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const AnimationTiming& aTiming,
|
||||
const TimingParams& aTiming,
|
||||
dom::ComputedTimingProperties& aRetVal)
|
||||
{
|
||||
// AnimationEffectTimingProperties
|
||||
aRetVal.mDelay = aTiming.mDelay.ToMilliseconds();
|
||||
aRetVal.mFill = aTiming.mFillMode;
|
||||
aRetVal.mIterations = aTiming.mIterationCount;
|
||||
aRetVal.mDuration.SetAsUnrestrictedDouble() = aTiming.mIterationDuration.ToMilliseconds();
|
||||
aRetVal.mFill = aComputedTiming.mFill;
|
||||
aRetVal.mIterations = aComputedTiming.mIterations;
|
||||
aRetVal.mDuration.SetAsUnrestrictedDouble() =
|
||||
aComputedTiming.mDuration.ToMilliseconds();
|
||||
aRetVal.mDirection = aTiming.mDirection;
|
||||
|
||||
// ComputedTimingProperties
|
||||
@@ -89,14 +75,15 @@ KeyframeEffectReadOnly::KeyframeEffectReadOnly(
|
||||
nsIDocument* aDocument,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
const AnimationTiming& aTiming)
|
||||
const TimingParams& aTiming)
|
||||
: AnimationEffectReadOnly(aDocument)
|
||||
, mTarget(aTarget)
|
||||
, mTiming(aTiming)
|
||||
, mPseudoType(aPseudoType)
|
||||
, mInEffectOnLastAnimationTimingUpdate(false)
|
||||
{
|
||||
MOZ_ASSERT(aTarget, "null animation target is not yet supported");
|
||||
|
||||
mTiming = new AnimationEffectTimingReadOnly(aTiming);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@@ -118,13 +105,20 @@ KeyframeEffectReadOnly::Composite() const
|
||||
return CompositeOperation::Replace;
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::SetTiming(const AnimationTiming& aTiming)
|
||||
already_AddRefed<AnimationEffectTimingReadOnly>
|
||||
KeyframeEffectReadOnly::Timing() const
|
||||
{
|
||||
if (mTiming == aTiming) {
|
||||
RefPtr<AnimationEffectTimingReadOnly> temp(mTiming);
|
||||
return temp.forget();
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::SetSpecifiedTiming(const TimingParams& aTiming)
|
||||
{
|
||||
if (mTiming->AsTimingParams() == aTiming) {
|
||||
return;
|
||||
}
|
||||
mTiming = aTiming;
|
||||
mTiming->SetTimingParams(aTiming);
|
||||
if (mAnimation) {
|
||||
mAnimation->NotifyEffectTimingUpdated();
|
||||
}
|
||||
@@ -205,31 +199,36 @@ void
|
||||
KeyframeEffectReadOnly::GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const
|
||||
{
|
||||
const Nullable<TimeDuration> currentTime = GetLocalTime();
|
||||
GetComputedTimingDictionary(GetComputedTimingAt(currentTime, mTiming),
|
||||
GetComputedTimingDictionary(GetComputedTimingAt(currentTime,
|
||||
SpecifiedTiming()),
|
||||
currentTime,
|
||||
mTiming,
|
||||
SpecifiedTiming(),
|
||||
aRetVal);
|
||||
}
|
||||
|
||||
ComputedTiming
|
||||
KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
const Nullable<TimeDuration>& aLocalTime,
|
||||
const AnimationTiming& aTiming)
|
||||
const TimingParams& aTiming)
|
||||
{
|
||||
const TimeDuration zeroDuration;
|
||||
|
||||
// Currently we expect negative durations to be picked up during CSS
|
||||
// parsing but when we start receiving timing parameters from other sources
|
||||
// we will need to clamp negative durations here.
|
||||
// For now, if we're hitting this it probably means we're overflowing
|
||||
// integer arithmetic in mozilla::TimeStamp.
|
||||
MOZ_ASSERT(aTiming.mIterationDuration >= zeroDuration,
|
||||
"Expecting iteration duration >= 0");
|
||||
const StickyTimeDuration zeroDuration;
|
||||
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
ComputedTiming result;
|
||||
|
||||
result.mActiveDuration = ActiveDuration(aTiming);
|
||||
if (aTiming.mDuration.IsUnrestrictedDouble()) {
|
||||
double durationMs = aTiming.mDuration.GetAsUnrestrictedDouble();
|
||||
if (!IsNaN(durationMs) && durationMs >= 0.0f) {
|
||||
result.mDuration = StickyTimeDuration::FromMilliseconds(durationMs);
|
||||
}
|
||||
}
|
||||
result.mIterations = IsNaN(aTiming.mIterations) || aTiming.mIterations < 0.0f ?
|
||||
1.0f :
|
||||
aTiming.mIterations;
|
||||
result.mActiveDuration = ActiveDuration(result.mDuration, result.mIterations);
|
||||
result.mFill = aTiming.mFill == dom::FillMode::Auto ?
|
||||
dom::FillMode::None :
|
||||
aTiming.mFill;
|
||||
|
||||
// The default constructor for ComputedTiming sets all other members to
|
||||
// values consistent with an animation that has not been sampled.
|
||||
@@ -247,7 +246,7 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
StickyTimeDuration activeTime;
|
||||
if (localTime >= aTiming.mDelay + result.mActiveDuration) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::After;
|
||||
if (!aTiming.FillsForwards()) {
|
||||
if (!result.FillsForwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
result.mProgress.SetNull();
|
||||
return result;
|
||||
@@ -255,12 +254,11 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
activeTime = result.mActiveDuration;
|
||||
// Note that infinity == floor(infinity) so this will also be true when we
|
||||
// have finished an infinitely repeating animation of zero duration.
|
||||
isEndOfFinalIteration =
|
||||
aTiming.mIterationCount != 0.0 &&
|
||||
aTiming.mIterationCount == floor(aTiming.mIterationCount);
|
||||
isEndOfFinalIteration = result.mIterations != 0.0 &&
|
||||
result.mIterations == floor(result.mIterations);
|
||||
} else if (localTime < aTiming.mDelay) {
|
||||
result.mPhase = ComputedTiming::AnimationPhase::Before;
|
||||
if (!aTiming.FillsBackwards()) {
|
||||
if (!result.FillsBackwards()) {
|
||||
// The animation isn't active or filling at this time.
|
||||
result.mProgress.SetNull();
|
||||
return result;
|
||||
@@ -275,19 +273,19 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
|
||||
// Get the position within the current iteration.
|
||||
StickyTimeDuration iterationTime;
|
||||
if (aTiming.mIterationDuration != zeroDuration) {
|
||||
if (result.mDuration != zeroDuration) {
|
||||
iterationTime = isEndOfFinalIteration
|
||||
? StickyTimeDuration(aTiming.mIterationDuration)
|
||||
: activeTime % aTiming.mIterationDuration;
|
||||
? result.mDuration
|
||||
: activeTime % result.mDuration;
|
||||
} /* else, iterationTime is zero */
|
||||
|
||||
// Determine the 0-based index of the current iteration.
|
||||
if (isEndOfFinalIteration) {
|
||||
result.mCurrentIteration =
|
||||
aTiming.mIterationCount == NS_IEEEPositiveInfinity()
|
||||
IsInfinite(result.mIterations) // Positive Infinity?
|
||||
? UINT64_MAX // In GetComputedTimingDictionary(), we will convert this
|
||||
// into Infinity.
|
||||
: static_cast<uint64_t>(aTiming.mIterationCount) - 1;
|
||||
: static_cast<uint64_t>(result.mIterations) - 1;
|
||||
} else if (activeTime == zeroDuration) {
|
||||
// If the active time is zero we're either in the first iteration
|
||||
// (including filling backwards) or we have finished an animation with an
|
||||
@@ -295,11 +293,11 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
// the exact end of an iteration since we deal with that above).
|
||||
result.mCurrentIteration =
|
||||
result.mPhase == ComputedTiming::AnimationPhase::After
|
||||
? static_cast<uint64_t>(aTiming.mIterationCount) // floor
|
||||
? static_cast<uint64_t>(result.mIterations) // floor
|
||||
: 0;
|
||||
} else {
|
||||
result.mCurrentIteration =
|
||||
static_cast<uint64_t>(activeTime / aTiming.mIterationDuration); // floor
|
||||
static_cast<uint64_t>(activeTime / result.mDuration); // floor
|
||||
}
|
||||
|
||||
// Normalize the iteration time into a fraction of the iteration duration.
|
||||
@@ -308,15 +306,15 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
} else if (result.mPhase == ComputedTiming::AnimationPhase::After) {
|
||||
double progress = isEndOfFinalIteration
|
||||
? 1.0
|
||||
: fmod(aTiming.mIterationCount, 1.0f);
|
||||
: fmod(result.mIterations, 1.0);
|
||||
result.mProgress.SetValue(progress);
|
||||
} else {
|
||||
// We are in the active phase so the iteration duration can't be zero.
|
||||
MOZ_ASSERT(aTiming.mIterationDuration != zeroDuration,
|
||||
MOZ_ASSERT(result.mDuration != zeroDuration,
|
||||
"In the active phase of a zero-duration animation?");
|
||||
double progress = aTiming.mIterationDuration == TimeDuration::Forever()
|
||||
double progress = result.mDuration == StickyTimeDuration::Forever()
|
||||
? 0.0
|
||||
: iterationTime / aTiming.mIterationDuration;
|
||||
: iterationTime / result.mDuration;
|
||||
result.mProgress.SetValue(progress);
|
||||
}
|
||||
|
||||
@@ -341,23 +339,28 @@ KeyframeEffectReadOnly::GetComputedTimingAt(
|
||||
result.mProgress.SetValue(1.0 - result.mProgress.Value());
|
||||
}
|
||||
|
||||
if (aTiming.mFunction) {
|
||||
result.mProgress.SetValue(
|
||||
aTiming.mFunction->GetValue(result.mProgress.Value()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StickyTimeDuration
|
||||
KeyframeEffectReadOnly::ActiveDuration(const AnimationTiming& aTiming)
|
||||
KeyframeEffectReadOnly::ActiveDuration(const StickyTimeDuration& aIterationDuration,
|
||||
double aIterationCount)
|
||||
{
|
||||
if (aTiming.mIterationCount == mozilla::PositiveInfinity<float>()) {
|
||||
if (IsInfinite(aIterationCount)) {
|
||||
// An animation that repeats forever has an infinite active duration
|
||||
// unless its iteration duration is zero, in which case it has a zero
|
||||
// active duration.
|
||||
const StickyTimeDuration zeroDuration;
|
||||
return aTiming.mIterationDuration == zeroDuration
|
||||
? zeroDuration
|
||||
: StickyTimeDuration::Forever();
|
||||
return aIterationDuration == zeroDuration ?
|
||||
zeroDuration :
|
||||
StickyTimeDuration::Forever();
|
||||
}
|
||||
return StickyTimeDuration(
|
||||
aTiming.mIterationDuration.MultDouble(aTiming.mIterationCount));
|
||||
return aIterationDuration.MultDouble(aIterationCount);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/web-animations/#in-play
|
||||
@@ -466,11 +469,6 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!computedTiming.mProgress.IsNull() &&
|
||||
0.0 <= computedTiming.mProgress.Value() &&
|
||||
computedTiming.mProgress.Value() <= 1.0,
|
||||
"iteration progress should be in [0-1]");
|
||||
|
||||
for (size_t propIdx = 0, propEnd = mProperties.Length();
|
||||
propIdx != propEnd; ++propIdx)
|
||||
{
|
||||
@@ -508,16 +506,12 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
*segmentEnd = segment + prop.mSegments.Length();
|
||||
while (segment->mToKey < computedTiming.mProgress.Value()) {
|
||||
MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
|
||||
++segment;
|
||||
if (segment == segmentEnd) {
|
||||
MOZ_ASSERT_UNREACHABLE("incorrect iteration progress");
|
||||
break; // in order to continue in outer loop (just below)
|
||||
if ((segment+1) == segmentEnd) {
|
||||
break;
|
||||
}
|
||||
++segment;
|
||||
MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys");
|
||||
}
|
||||
if (segment == segmentEnd) {
|
||||
continue;
|
||||
}
|
||||
MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys");
|
||||
MOZ_ASSERT(segment >= prop.mSegments.Elements() &&
|
||||
size_t(segment - prop.mSegments.Elements()) <
|
||||
@@ -533,7 +527,8 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr<AnimValuesStyleRule>& aStyleRule,
|
||||
(computedTiming.mProgress.Value() - segment->mFromKey) /
|
||||
(segment->mToKey - segment->mFromKey);
|
||||
double valuePosition =
|
||||
segment->mTimingFunction.GetValue(positionInSegment);
|
||||
ComputedTimingFunction::GetPortion(segment->mTimingFunction,
|
||||
positionInSegment);
|
||||
|
||||
StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty);
|
||||
|
||||
@@ -641,55 +636,6 @@ DumpAnimationProperties(nsTArray<AnimationProperty>& aAnimationProperties)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Extract an iteration duration from an UnrestrictedDoubleOrXXX object.
|
||||
template <typename T>
|
||||
static TimeDuration
|
||||
GetIterationDuration(const T& aDuration) {
|
||||
// Always return the same object to benefit from return-value optimization.
|
||||
TimeDuration result;
|
||||
if (aDuration.IsUnrestrictedDouble()) {
|
||||
double durationMs = aDuration.GetAsUnrestrictedDouble();
|
||||
if (!IsNaN(durationMs) && durationMs >= 0.0f) {
|
||||
result = TimeDuration::FromMilliseconds(durationMs);
|
||||
}
|
||||
}
|
||||
// else, aDuration should be zero
|
||||
return result;
|
||||
}
|
||||
|
||||
/* static */ AnimationTiming
|
||||
KeyframeEffectReadOnly::ConvertKeyframeEffectOptions(
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
|
||||
{
|
||||
AnimationTiming animationTiming;
|
||||
|
||||
if (aOptions.IsKeyframeEffectOptions()) {
|
||||
const KeyframeEffectOptions& opt = aOptions.GetAsKeyframeEffectOptions();
|
||||
|
||||
animationTiming.mIterationDuration = GetIterationDuration(opt.mDuration);
|
||||
animationTiming.mDelay = TimeDuration::FromMilliseconds(opt.mDelay);
|
||||
// FIXME: Covert mIterationCount to a valid value.
|
||||
// Bug 1214536 should revise this and keep the original value, so
|
||||
// AnimationTimingEffectReadOnly can get the original iterations.
|
||||
animationTiming.mIterationCount = (IsNaN(opt.mIterations) ||
|
||||
opt.mIterations < 0.0f) ?
|
||||
1.0f :
|
||||
opt.mIterations;
|
||||
animationTiming.mDirection = opt.mDirection;
|
||||
// FIXME: We should store original value.
|
||||
animationTiming.mFillMode = (opt.mFill == FillMode::Auto) ?
|
||||
FillMode::None :
|
||||
opt.mFill;
|
||||
} else {
|
||||
animationTiming.mIterationDuration = GetIterationDuration(aOptions);
|
||||
animationTiming.mDelay = TimeDuration(0);
|
||||
animationTiming.mIterationCount = 1.0f;
|
||||
animationTiming.mDirection = PlaybackDirection::Normal;
|
||||
animationTiming.mFillMode = FillMode::None;
|
||||
}
|
||||
return animationTiming;
|
||||
}
|
||||
|
||||
/**
|
||||
* A property and StyleAnimationValue pair.
|
||||
*/
|
||||
@@ -717,7 +663,7 @@ enum class ValuePosition
|
||||
struct OrderedKeyframeValueEntry : KeyframeValue
|
||||
{
|
||||
float mOffset;
|
||||
const ComputedTimingFunction* mTimingFunction;
|
||||
const Maybe<ComputedTimingFunction>* mTimingFunction;
|
||||
ValuePosition mPosition;
|
||||
|
||||
bool SameKeyframe(const OrderedKeyframeValueEntry& aOther) const
|
||||
@@ -752,7 +698,9 @@ struct OrderedKeyframeValueEntry : KeyframeValue
|
||||
// Third, by easing.
|
||||
if (aLhs.mTimingFunction) {
|
||||
if (aRhs.mTimingFunction) {
|
||||
int32_t order = aLhs.mTimingFunction->Compare(*aRhs.mTimingFunction);
|
||||
int32_t order =
|
||||
ComputedTimingFunction::Compare(*aLhs.mTimingFunction,
|
||||
*aRhs.mTimingFunction);
|
||||
if (order != 0) {
|
||||
return order < 0;
|
||||
}
|
||||
@@ -783,7 +731,7 @@ struct OrderedKeyframeValueEntry : KeyframeValue
|
||||
struct KeyframeValueEntry : KeyframeValue
|
||||
{
|
||||
float mOffset;
|
||||
ComputedTimingFunction mTimingFunction;
|
||||
Maybe<ComputedTimingFunction> mTimingFunction;
|
||||
|
||||
struct PropertyOffsetComparator
|
||||
{
|
||||
@@ -903,64 +851,6 @@ struct OffsetIndexedKeyframe
|
||||
nsTArray<PropertyValuesPair> mPropertyValuePairs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses a CSS <single-transition-timing-function> value from
|
||||
* aEasing into a ComputedTimingFunction. If parsing fails, aResult will
|
||||
* be set to 'linear'.
|
||||
*/
|
||||
static void
|
||||
ParseEasing(Element* aTarget,
|
||||
const nsAString& aEasing,
|
||||
ComputedTimingFunction& aResult)
|
||||
{
|
||||
nsIDocument* doc = aTarget->OwnerDoc();
|
||||
|
||||
nsCSSValue value;
|
||||
nsCSSParser parser;
|
||||
parser.ParseLonghandProperty(eCSSProperty_animation_timing_function,
|
||||
aEasing,
|
||||
doc->GetDocumentURI(),
|
||||
doc->GetDocumentURI(),
|
||||
doc->NodePrincipal(),
|
||||
value);
|
||||
|
||||
switch (value.GetUnit()) {
|
||||
case eCSSUnit_List: {
|
||||
const nsCSSValueList* list = value.GetListValue();
|
||||
if (list->mNext) {
|
||||
// don't support a list of timing functions
|
||||
break;
|
||||
}
|
||||
switch (list->mValue.GetUnit()) {
|
||||
case eCSSUnit_Enumerated:
|
||||
case eCSSUnit_Cubic_Bezier:
|
||||
case eCSSUnit_Steps: {
|
||||
nsTimingFunction timingFunction;
|
||||
nsRuleNode::ComputeTimingFunction(list->mValue, timingFunction);
|
||||
aResult.Init(timingFunction);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function list "
|
||||
"item unit");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case eCSSUnit_Null:
|
||||
case eCSSUnit_Inherit:
|
||||
case eCSSUnit_Initial:
|
||||
case eCSSUnit_Unset:
|
||||
case eCSSUnit_TokenStream:
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unexpected animation-timing-function unit");
|
||||
break;
|
||||
}
|
||||
|
||||
aResult.Init(nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR));
|
||||
}
|
||||
|
||||
/**
|
||||
* An additional property (for a property-values pair) found on a Keyframe
|
||||
* or PropertyIndexedKeyframes object.
|
||||
@@ -1087,7 +977,11 @@ GetPropertyValuesPairs(JSContext* aCx,
|
||||
nsCSSProps::LookupPropertyByIDLName(propName,
|
||||
nsCSSProps::eEnabledForAllContent);
|
||||
if (property != eCSSProperty_UNKNOWN &&
|
||||
nsCSSProps::kAnimTypeTable[property] != eStyleAnimType_None) {
|
||||
(nsCSSProps::IsShorthand(property) ||
|
||||
nsCSSProps::kAnimTypeTable[property] != eStyleAnimType_None)) {
|
||||
// Only need to check for longhands being animatable, as the
|
||||
// StyleAnimationValue::ComputeValues calls later on will check for
|
||||
// a shorthand's components being animatable.
|
||||
AdditionalProperty* p = properties.AppendElement();
|
||||
p->mProperty = property;
|
||||
p->mJsidIndex = i;
|
||||
@@ -1233,12 +1127,14 @@ ApplyDistributeSpacing(nsTArray<OffsetIndexedKeyframe>& aKeyframes)
|
||||
* objects.
|
||||
*
|
||||
* @param aTarget The target of the animation.
|
||||
* @param aPseudoType The pseudo type of the target if it is a pseudo element.
|
||||
* @param aKeyframes The keyframes to read.
|
||||
* @param aResult The array to append the resulting KeyframeValueEntry
|
||||
* objects to.
|
||||
*/
|
||||
static void
|
||||
GenerateValueEntries(Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
nsTArray<OffsetIndexedKeyframe>& aKeyframes,
|
||||
nsTArray<KeyframeValueEntry>& aResult,
|
||||
ErrorResult& aRv)
|
||||
@@ -1249,8 +1145,10 @@ GenerateValueEntries(Element* aTarget,
|
||||
|
||||
for (OffsetIndexedKeyframe& keyframe : aKeyframes) {
|
||||
float offset = float(keyframe.mKeyframeDict.mOffset.Value());
|
||||
ComputedTimingFunction easing;
|
||||
ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing, easing);
|
||||
// ParseEasing uses element's owner doc, so if it is a pseudo element,
|
||||
// we use its parent element's owner doc.
|
||||
Maybe<ComputedTimingFunction> easing =
|
||||
AnimationUtils::ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing);
|
||||
// We ignore keyframe.mKeyframeDict.mComposite since we don't support
|
||||
// composite modes on keyframes yet.
|
||||
|
||||
@@ -1286,6 +1184,7 @@ GenerateValueEntries(Element* aTarget,
|
||||
if (StyleAnimationValue::ComputeValues(pair.mProperty,
|
||||
nsCSSProps::eEnabledForAllContent,
|
||||
aTarget,
|
||||
aPseudoType,
|
||||
pair.mValues[0],
|
||||
/* aUseSVGMode */ false,
|
||||
values)) {
|
||||
@@ -1439,13 +1338,14 @@ static void
|
||||
BuildAnimationPropertyListFromKeyframeSequence(
|
||||
JSContext* aCx,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
JS::ForOfIterator& aIterator,
|
||||
nsTArray<AnimationProperty>& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
// Convert the object in aIterator to sequence<Keyframe>, producing
|
||||
// an array of OffsetIndexedKeyframe objects.
|
||||
nsAutoTArray<OffsetIndexedKeyframe,4> keyframes;
|
||||
AutoTArray<OffsetIndexedKeyframe,4> keyframes;
|
||||
if (!ConvertKeyframeSequence(aCx, aIterator, keyframes)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
@@ -1472,7 +1372,7 @@ BuildAnimationPropertyListFromKeyframeSequence(
|
||||
// Convert the OffsetIndexedKeyframes into a list of KeyframeValueEntry
|
||||
// objects.
|
||||
nsTArray<KeyframeValueEntry> entries;
|
||||
GenerateValueEntries(aTarget, keyframes, entries, aRv);
|
||||
GenerateValueEntries(aTarget, aPseudoType, keyframes, entries, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
@@ -1496,6 +1396,7 @@ static void
|
||||
BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
JSContext* aCx,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
JS::Handle<JS::Value> aValue,
|
||||
InfallibleTArray<AnimationProperty>& aResult,
|
||||
ErrorResult& aRv)
|
||||
@@ -1511,8 +1412,10 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
return;
|
||||
}
|
||||
|
||||
ComputedTimingFunction easing;
|
||||
ParseEasing(aTarget, keyframes.mEasing, easing);
|
||||
// ParseEasing uses element's owner doc, so if it is a pseudo element,
|
||||
// we use its parent element's owner doc.
|
||||
Maybe<ComputedTimingFunction> easing =
|
||||
AnimationUtils::ParseEasing(aTarget, keyframes.mEasing);
|
||||
|
||||
// We ignore easing.mComposite since we don't support composite modes on
|
||||
// keyframes yet.
|
||||
@@ -1566,6 +1469,7 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
if (!StyleAnimationValue::ComputeValues(pair.mProperty,
|
||||
nsCSSProps::eEnabledForAllContent,
|
||||
aTarget,
|
||||
aPseudoType,
|
||||
pair.mValues[0],
|
||||
/* aUseSVGMode */ false,
|
||||
fromValues)) {
|
||||
@@ -1618,6 +1522,7 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
if (!StyleAnimationValue::ComputeValues(pair.mProperty,
|
||||
nsCSSProps::eEnabledForAllContent,
|
||||
aTarget,
|
||||
aPseudoType,
|
||||
pair.mValues[i + 1],
|
||||
/* aUseSVGMode */ false,
|
||||
toValues)) {
|
||||
@@ -1666,7 +1571,8 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes(
|
||||
KeyframeEffectReadOnly::BuildAnimationPropertyList(
|
||||
JSContext* aCx,
|
||||
Element* aTarget,
|
||||
const Optional<JS::Handle<JSObject*>>& aFrames,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
InfallibleTArray<AnimationProperty>& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
@@ -1682,17 +1588,16 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList(
|
||||
// we can look at the open-ended set of properties on a
|
||||
// PropertyIndexedKeyframes or Keyframe.
|
||||
|
||||
if (!aFrames.WasPassed() || !aFrames.Value().get()) {
|
||||
// The argument was omitted, or was explicitly null. In both cases,
|
||||
// the default dictionary value for PropertyIndexedKeyframes would
|
||||
// result in no keyframes.
|
||||
if (!aFrames) {
|
||||
// The argument was explicitly null. In this case, the default dictionary
|
||||
// value for PropertyIndexedKeyframes would result in no keyframes.
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we know we have an object. We try to convert it to a
|
||||
// sequence<Keyframe> first, and if that fails due to not being iterable,
|
||||
// we try to convert it to PropertyIndexedKeyframes.
|
||||
JS::Rooted<JS::Value> objectValue(aCx, JS::ObjectValue(*aFrames.Value()));
|
||||
JS::Rooted<JS::Value> objectValue(aCx, JS::ObjectValue(*aFrames));
|
||||
JS::ForOfIterator iter(aCx);
|
||||
if (!iter.init(objectValue, JS::ForOfIterator::AllowNonIterable)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
@@ -1700,10 +1605,11 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList(
|
||||
}
|
||||
|
||||
if (iter.valueIsIterable()) {
|
||||
BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, iter,
|
||||
aResult, aRv);
|
||||
BuildAnimationPropertyListFromKeyframeSequence(aCx, aTarget, aPseudoType,
|
||||
iter, aResult, aRv);
|
||||
} else {
|
||||
BuildAnimationPropertyListFromPropertyIndexedKeyframes(aCx, aTarget,
|
||||
aPseudoType,
|
||||
objectValue, aResult,
|
||||
aRv);
|
||||
}
|
||||
@@ -1712,35 +1618,78 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList(
|
||||
/* static */ already_AddRefed<KeyframeEffectReadOnly>
|
||||
KeyframeEffectReadOnly::Constructor(
|
||||
const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
const Optional<JS::Handle<JSObject*>>& aFrames,
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
const TimingParams& aTiming,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (!aTarget) {
|
||||
if (aTarget.IsNull()) {
|
||||
// We don't support null targets yet.
|
||||
aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AnimationTiming timing = ConvertKeyframeEffectOptions(aOptions);
|
||||
const ElementOrCSSPseudoElement& target = aTarget.Value();
|
||||
MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
|
||||
"Uninitialized target");
|
||||
|
||||
RefPtr<Element> targetElement;
|
||||
nsCSSPseudoElements::Type pseudoType =
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement;
|
||||
if (target.IsElement()) {
|
||||
targetElement = &target.GetAsElement();
|
||||
} else {
|
||||
targetElement = target.GetAsCSSPseudoElement().ParentElement();
|
||||
pseudoType = target.GetAsCSSPseudoElement().GetType();
|
||||
}
|
||||
|
||||
if (!targetElement->GetCurrentDoc()) {
|
||||
// Bug 1245748: We don't support targets that are not in a document yet.
|
||||
aRv.Throw(NS_ERROR_DOM_ANIM_TARGET_NOT_IN_DOC_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InfallibleTArray<AnimationProperty> animationProperties;
|
||||
BuildAnimationPropertyList(aGlobal.Context(), aTarget, aFrames,
|
||||
animationProperties, aRv);
|
||||
BuildAnimationPropertyList(aGlobal.Context(), targetElement, pseudoType,
|
||||
aFrames, animationProperties, aRv);
|
||||
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<KeyframeEffectReadOnly> effect =
|
||||
new KeyframeEffectReadOnly(aTarget->OwnerDoc(), aTarget,
|
||||
nsCSSPseudoElements::ePseudo_NotPseudoElement,
|
||||
timing);
|
||||
new KeyframeEffectReadOnly(targetElement->OwnerDoc(), targetElement,
|
||||
pseudoType, aTiming);
|
||||
effect->mProperties = Move(animationProperties);
|
||||
return effect.forget();
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::GetTarget(
|
||||
Nullable<OwningElementOrCSSPseudoElement>& aRv) const
|
||||
{
|
||||
if (!mTarget) {
|
||||
aRv.SetNull();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mPseudoType) {
|
||||
case nsCSSPseudoElements::ePseudo_before:
|
||||
case nsCSSPseudoElements::ePseudo_after:
|
||||
aRv.SetValue().SetAsCSSPseudoElement() =
|
||||
CSSPseudoElement::GetCSSPseudoElement(mTarget, mPseudoType);
|
||||
break;
|
||||
|
||||
case nsCSSPseudoElements::ePseudo_NotPseudoElement:
|
||||
aRv.SetValue().SetAsElement() = mTarget;
|
||||
break;
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("Animation of unsupported pseudo-type");
|
||||
aRv.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
|
||||
nsTArray<JSObject*>& aResult,
|
||||
@@ -1782,8 +1731,8 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
|
||||
entry->mProperty = property.mProperty;
|
||||
entry->mValue = segment.mToValue;
|
||||
entry->mOffset = segment.mToKey;
|
||||
entry->mTimingFunction =
|
||||
segment.mToKey == 1.0f ? nullptr : &segment.mTimingFunction;
|
||||
entry->mTimingFunction = segment.mToKey == 1.0f ?
|
||||
nullptr : &segment.mTimingFunction;
|
||||
entry->mPosition =
|
||||
segment.mFromKey == segment.mToKey && segment.mToKey == 1.0f ?
|
||||
ValuePosition::Last :
|
||||
@@ -1802,10 +1751,10 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx,
|
||||
ComputedKeyframe keyframeDict;
|
||||
keyframeDict.mOffset.SetValue(entry->mOffset);
|
||||
keyframeDict.mComputedOffset.Construct(entry->mOffset);
|
||||
if (entry->mTimingFunction) {
|
||||
if (entry->mTimingFunction && entry->mTimingFunction->isSome()) {
|
||||
// If null, leave easing as its default "linear".
|
||||
keyframeDict.mEasing.Truncate();
|
||||
entry->mTimingFunction->AppendToString(keyframeDict.mEasing);
|
||||
entry->mTimingFunction->value().AppendToString(keyframeDict.mEasing);
|
||||
}
|
||||
keyframeDict.mComposite.SetValue(CompositeOperation::Replace);
|
||||
|
||||
|
||||
@@ -15,14 +15,17 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
|
||||
#include "mozilla/LayerAnimationInfo.h" // LayerAnimations::kRecords
|
||||
#include "mozilla/OwningNonNull.h" // OwningNonNull<...>
|
||||
#include "mozilla/StickyTimeDuration.h"
|
||||
#include "mozilla/StyleAnimationValue.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/dom/AnimationEffectReadOnly.h"
|
||||
#include "mozilla/dom/AnimationEffectTimingReadOnly.h" // TimingParams
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/KeyframeBinding.h"
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
|
||||
|
||||
struct JSContext;
|
||||
class nsCSSPropertySet;
|
||||
class nsIContent;
|
||||
@@ -36,41 +39,13 @@ struct AnimationCollection;
|
||||
class AnimValuesStyleRule;
|
||||
|
||||
namespace dom {
|
||||
struct ComputedTimingProperties;
|
||||
class ElementOrCSSPseudoElement;
|
||||
class OwningElementOrCSSPseudoElement;
|
||||
class UnrestrictedDoubleOrKeyframeEffectOptions;
|
||||
enum class IterationCompositeOperation : uint32_t;
|
||||
enum class CompositeOperation : uint32_t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Input timing parameters.
|
||||
*
|
||||
* Eventually this will represent all the input timing parameters specified
|
||||
* by content but for now it encapsulates just the subset of those
|
||||
* parameters passed to GetPositionInIteration.
|
||||
*/
|
||||
struct AnimationTiming
|
||||
{
|
||||
TimeDuration mIterationDuration;
|
||||
TimeDuration mDelay;
|
||||
float mIterationCount; // mozilla::PositiveInfinity<float>() means infinite
|
||||
dom::PlaybackDirection mDirection;
|
||||
dom::FillMode mFillMode;
|
||||
|
||||
bool FillsForwards() const;
|
||||
bool FillsBackwards() const;
|
||||
bool operator==(const AnimationTiming& aOther) const {
|
||||
return mIterationDuration == aOther.mIterationDuration &&
|
||||
mDelay == aOther.mDelay &&
|
||||
mIterationCount == aOther.mIterationCount &&
|
||||
mDirection == aOther.mDirection &&
|
||||
mFillMode == aOther.mFillMode;
|
||||
}
|
||||
bool operator!=(const AnimationTiming& aOther) const {
|
||||
return !(*this == aOther);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores the results of calculating the timing properties of an animation
|
||||
* at a given sample time.
|
||||
@@ -88,6 +63,25 @@ struct ComputedTiming
|
||||
Nullable<double> mProgress;
|
||||
// Zero-based iteration index (meaningless if mProgress is null).
|
||||
uint64_t mCurrentIteration = 0;
|
||||
// Unlike TimingParams::mIterations, this value is
|
||||
// guaranteed to be in the range [0, Infinity].
|
||||
double mIterations = 1.0;
|
||||
StickyTimeDuration mDuration;
|
||||
|
||||
// This is the computed fill mode so it is never auto
|
||||
dom::FillMode mFill = dom::FillMode::None;
|
||||
bool FillsForwards() const {
|
||||
MOZ_ASSERT(mFill != dom::FillMode::Auto,
|
||||
"mFill should not be Auto in ComputedTiming.");
|
||||
return mFill == dom::FillMode::Both ||
|
||||
mFill == dom::FillMode::Forwards;
|
||||
}
|
||||
bool FillsBackwards() const {
|
||||
MOZ_ASSERT(mFill != dom::FillMode::Auto,
|
||||
"mFill should not be Auto in ComputedTiming.");
|
||||
return mFill == dom::FillMode::Both ||
|
||||
mFill == dom::FillMode::Backwards;
|
||||
}
|
||||
|
||||
enum class AnimationPhase {
|
||||
Null, // Not sampled (null sample time)
|
||||
@@ -102,7 +96,7 @@ struct AnimationPropertySegment
|
||||
{
|
||||
float mFromKey, mToKey;
|
||||
StyleAnimationValue mFromValue, mToValue;
|
||||
ComputedTimingFunction mTimingFunction;
|
||||
Maybe<ComputedTimingFunction> mTimingFunction;
|
||||
|
||||
bool operator==(const AnimationPropertySegment& aOther) const {
|
||||
return mFromKey == aOther.mFromKey &&
|
||||
@@ -178,7 +172,7 @@ public:
|
||||
KeyframeEffectReadOnly(nsIDocument* aDocument,
|
||||
Element* aTarget,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
const AnimationTiming& aTiming);
|
||||
const TimingParams& aTiming);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(KeyframeEffectReadOnly,
|
||||
@@ -196,19 +190,26 @@ public:
|
||||
// KeyframeEffectReadOnly interface
|
||||
static already_AddRefed<KeyframeEffectReadOnly>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
Element* aTarget,
|
||||
const Optional<JS::Handle<JSObject*>>& aFrames,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
Element* GetTarget() const {
|
||||
// Currently we never return animations from the API whose effect
|
||||
// targets a pseudo-element so this should never be called when
|
||||
// mPseudoType is not 'none' (see bug 1174575).
|
||||
MOZ_ASSERT(mPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement,
|
||||
"Requesting the target of a KeyframeEffect that targets a"
|
||||
" pseudo-element is not yet supported.");
|
||||
return mTarget;
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
return Constructor(aGlobal, aTarget, aFrames,
|
||||
TimingParams::FromOptionsUnion(aOptions, aTarget),
|
||||
aRv);
|
||||
}
|
||||
|
||||
// More generalized version for Animatable.animate.
|
||||
// Not exposed to content.
|
||||
static already_AddRefed<KeyframeEffectReadOnly>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const Nullable<ElementOrCSSPseudoElement>& aTarget,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
const TimingParams& aTiming,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
|
||||
void GetFrames(JSContext*& aCx,
|
||||
nsTArray<JSObject*>& aResult,
|
||||
ErrorResult& aRv);
|
||||
@@ -227,9 +228,13 @@ public:
|
||||
aRetVal.AssignLiteral("distribute");
|
||||
}
|
||||
|
||||
const AnimationTiming& Timing() const { return mTiming; }
|
||||
AnimationTiming& Timing() { return mTiming; }
|
||||
void SetTiming(const AnimationTiming& aTiming);
|
||||
already_AddRefed<AnimationEffectTimingReadOnly> Timing() const override;
|
||||
|
||||
const TimingParams& SpecifiedTiming() const
|
||||
{
|
||||
return mTiming->AsTimingParams();
|
||||
}
|
||||
void SetSpecifiedTiming(const TimingParams& aTiming);
|
||||
void NotifyAnimationTimingUpdated();
|
||||
|
||||
Nullable<TimeDuration> GetLocalTime() const;
|
||||
@@ -246,22 +251,25 @@ public:
|
||||
// (because it is not currently active and is not filling at this time).
|
||||
static ComputedTiming
|
||||
GetComputedTimingAt(const Nullable<TimeDuration>& aLocalTime,
|
||||
const AnimationTiming& aTiming);
|
||||
const TimingParams& aTiming);
|
||||
|
||||
// Shortcut for that gets the computed timing using the current local time as
|
||||
// calculated from the timeline time.
|
||||
ComputedTiming
|
||||
GetComputedTiming(const AnimationTiming* aTiming = nullptr) const
|
||||
GetComputedTiming(const TimingParams* aTiming = nullptr) const
|
||||
{
|
||||
return GetComputedTimingAt(GetLocalTime(), aTiming ? *aTiming : mTiming);
|
||||
return GetComputedTimingAt(GetLocalTime(),
|
||||
aTiming ? *aTiming : SpecifiedTiming());
|
||||
}
|
||||
|
||||
void
|
||||
GetComputedTimingAsDict(ComputedTimingProperties& aRetVal) const override;
|
||||
|
||||
// Return the duration of the active interval for the given timing parameters.
|
||||
// Return the duration of the active interval for the given duration and
|
||||
// iteration count.
|
||||
static StickyTimeDuration
|
||||
ActiveDuration(const AnimationTiming& aTiming);
|
||||
ActiveDuration(const StickyTimeDuration& aIterationDuration,
|
||||
double aIterationCount);
|
||||
|
||||
bool IsInPlay() const;
|
||||
bool IsCurrent() const;
|
||||
@@ -334,20 +342,18 @@ protected:
|
||||
// owning Animation's timing.
|
||||
void UpdateTargetRegistration();
|
||||
|
||||
static AnimationTiming ConvertKeyframeEffectOptions(
|
||||
const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions);
|
||||
|
||||
static void BuildAnimationPropertyList(
|
||||
JSContext* aCx,
|
||||
Element* aTarget,
|
||||
const Optional<JS::Handle<JSObject*>>& aFrames,
|
||||
nsCSSPseudoElements::Type aPseudoType,
|
||||
JS::Handle<JSObject*> aFrames,
|
||||
InfallibleTArray<AnimationProperty>& aResult,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<Element> mTarget;
|
||||
RefPtr<Animation> mAnimation;
|
||||
|
||||
AnimationTiming mTiming;
|
||||
OwningNonNull<AnimationEffectTimingReadOnly> mTiming;
|
||||
nsCSSPseudoElements::Type mPseudoType;
|
||||
|
||||
InfallibleTArray<AnimationProperty> mProperties;
|
||||
|
||||
@@ -63,6 +63,7 @@ PendingAnimationTracker::TriggerPendingAnimationsOnNextTick(const TimeStamp&
|
||||
// itself on the next tick where it has a timeline.
|
||||
if (!timeline) {
|
||||
iter.Remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
// When the timeline's refresh driver is under test control, its values
|
||||
|
||||
@@ -10,7 +10,9 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
|
||||
EXPORTS.mozilla.dom += [
|
||||
'Animation.h',
|
||||
'AnimationEffectReadOnly.h',
|
||||
'AnimationEffectTimingReadOnly.h',
|
||||
'AnimationTimeline.h',
|
||||
'CSSPseudoElement.h',
|
||||
'DocumentTimeline.h',
|
||||
'KeyframeEffect.h',
|
||||
]
|
||||
@@ -29,10 +31,12 @@ EXPORTS.mozilla += [
|
||||
UNIFIED_SOURCES += [
|
||||
'Animation.cpp',
|
||||
'AnimationEffectReadOnly.cpp',
|
||||
'AnimationEffectTimingReadOnly.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
'AnimationUtils.cpp',
|
||||
'AnimValuesStyleRule.cpp',
|
||||
'ComputedTimingFunction.cpp',
|
||||
'CSSPseudoElement.cpp',
|
||||
'DocumentTimeline.cpp',
|
||||
'EffectCompositor.cpp',
|
||||
'EffectSet.cpp',
|
||||
|
||||
@@ -3,6 +3,9 @@ support-files =
|
||||
testcommon.js
|
||||
../../imptests/testharness.js
|
||||
../../imptests/testharnessreport.js
|
||||
[chrome/test_animate_xrays.html]
|
||||
# file_animate_xrays.html needs to go in mochitest.ini since it is served
|
||||
# over HTTP
|
||||
[chrome/test_animation_observers.html]
|
||||
[chrome/test_restyles.html]
|
||||
[chrome/test_running_on_compositor.html]
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<script>
|
||||
Element.prototype.animate = function() {
|
||||
throw 'Called animate() as defined in content document';
|
||||
}
|
||||
// Bug 1211783: Use KeyframeEffect (not KeyframeEffectReadOnly) here
|
||||
for (var obj of [KeyframeEffectReadOnly, Animation]) {
|
||||
obj = function() {
|
||||
throw 'Called overridden ' + String(obj) + ' constructor';
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!doctype html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<script type="application/javascript" src="../testharness.js"></script>
|
||||
<script type="application/javascript" src="../testharnessreport.js"></script>
|
||||
<script type="application/javascript" src="../testcommon.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045994"
|
||||
target="_blank">Mozilla Bug 1045994</a>
|
||||
<div id="log"></div>
|
||||
<iframe id="iframe"
|
||||
src="http://example.org/tests/dom/animation/test/chrome/file_animate_xrays.html"></iframe>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
var win = document.getElementById('iframe').contentWindow;
|
||||
|
||||
async_test(function(t) {
|
||||
window.addEventListener('load', t.step_func(function() {
|
||||
var target = win.document.getElementById('target');
|
||||
var anim = target.animate({ opacity: [ 0, 1 ] }, 2000);
|
||||
// In the x-ray case, the frames object will be given an opaque wrapper
|
||||
// so it won't be possible to fetch any frames from it.
|
||||
assert_equals(anim.effect.getFrames().length, 0);
|
||||
t.done();
|
||||
}));
|
||||
}, 'Calling animate() across x-rays');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -763,6 +763,27 @@ function assert_records(expected, desc) {
|
||||
});
|
||||
});
|
||||
|
||||
addAsyncAnimTest("change_id", aOptions, function*() {
|
||||
e.style.animation = "anim 100s";
|
||||
|
||||
var animation = div.getAnimations()[0];
|
||||
yield await_frame();
|
||||
assert_records([{ added: [animation], changed: [], removed: []}],
|
||||
"records after creation");
|
||||
animation.id = "new id";
|
||||
yield await_frame();
|
||||
assert_records([{ added: [], changed: [animation], removed: []}],
|
||||
"records after id is changed");
|
||||
|
||||
animation.id = "new id";
|
||||
yield await_frame();
|
||||
assert_records([],
|
||||
"records after assigning same value with id");
|
||||
|
||||
e.style.animation = "";
|
||||
yield await_frame();
|
||||
});
|
||||
|
||||
// Test that making a redundant change to currentTime while an Animation
|
||||
// is pause-pending still generates a change MutationRecord since setting
|
||||
// the currentTime to any value in this state aborts the pending pause.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Bug 1216842: effect-level easing function produces negative values (compositor thread)</title>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces negative values in (0, 0.766312060) */
|
||||
easing: "cubic-bezier(0,-0.5,1,-0.5)",
|
||||
duration: 100,
|
||||
iterations: 0.75 /* To finish in the extraporation range */
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.play();
|
||||
animation.finished.then(function() {
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Bug 1216842: effect-level easing function produces values greater than 1 (compositor thread)</title>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ opacity: [0, 1] },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces values greater than 1 in (0.23368794, 1) */
|
||||
easing: "cubic-bezier(0,1.5,1,1.5)",
|
||||
duration: 100,
|
||||
iterations: 0.25 /* To finish in the extraporation range */
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.play();
|
||||
animation.finished.then(function() {
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Bug 1216842: effect-level easing function produces values greater than 1 (main-thread)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ color: ["red", "blue"] },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces values greater than 1 in (0.23368794, 1) */
|
||||
easing: "cubic-bezier(0,1.5,1,1.5)",
|
||||
duration: 100
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.pause();
|
||||
animation.currentTime = 250;
|
||||
document.documentElement.className = "";
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>Bug 1216842: effect-level easing function produces negative values (main-thread)</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ color: ["red", "blue"] },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces negative values in (0, 0.766312060) */
|
||||
easing: "cubic-bezier(0,-0.5,1,-0.5)",
|
||||
duration: 100
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.pause();
|
||||
animation.currentTime = 250;
|
||||
document.documentElement.className = "";
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>
|
||||
Bug 1216842: effect-level easing function produces negative values passed
|
||||
to step-end function (compositor thread)
|
||||
</title>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ opacity: [0, 1], easing: "step-end" },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces negative values in (0, 0.766312060) */
|
||||
easing: "cubic-bezier(0,-0.5,1,-0.5)",
|
||||
duration: 100,
|
||||
iterations: 0.75 /* To finish in the extraporation range */
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.play();
|
||||
animation.finished.then(function() {
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!doctype html>
|
||||
<html class="reftest-wait">
|
||||
<head>
|
||||
<title>
|
||||
Bug 1216842: effect-level easing function produces values greater than 1
|
||||
which are passed to step-end function (compositor thread)
|
||||
</title>
|
||||
<style>
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
<script>
|
||||
var target = document.getElementById("target");
|
||||
var effect =
|
||||
new KeyframeEffectReadOnly(
|
||||
target,
|
||||
{ opacity: [0, 1], easing: "step-end" },
|
||||
{
|
||||
fill: "forwards",
|
||||
/* The function produces values greater than 1 in (0.23368794, 1) */
|
||||
easing: "cubic-bezier(0,1.5,1,1.5)",
|
||||
duration: 100,
|
||||
iterations: 0.25 /* To finish in the extraporation range */
|
||||
}
|
||||
);
|
||||
var animation = new Animation(effect, document.timeline);
|
||||
animation.play();
|
||||
animation.finished.then(function() {
|
||||
document.documentElement.className = "";
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Bug 1239889</title>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
<script>
|
||||
var div = document.createElement('div');
|
||||
var effect = new KeyframeEffectReadOnly(div, { opacity: [0, 1] });
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,3 @@
|
||||
<div id=target><script>
|
||||
var player = target.animate([{background: 'green'}, {background: 'green'}]);
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
load 1239889-1.html
|
||||
load 1244595-1.html
|
||||
load 1216842-1.html
|
||||
load 1216842-2.html
|
||||
load 1216842-3.html
|
||||
load 1216842-4.html
|
||||
load 1216842-5.html
|
||||
load 1216842-6.html
|
||||
@@ -0,0 +1,27 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="../testcommon.js"></script>
|
||||
<style>
|
||||
@keyframes abc { }
|
||||
</style>
|
||||
<body>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var animation = div.getAnimations()[0];
|
||||
|
||||
assert_equals(animation.id, '', 'id for CSS Animation is initially empty');
|
||||
|
||||
animation.id = 'anim'
|
||||
|
||||
assert_equals(animation.id, 'anim', 'animation.id reflects the value set');
|
||||
}, 'Animation.id for CSS Animations');
|
||||
|
||||
done();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+22
-28
@@ -24,7 +24,7 @@
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a document'
|
||||
+ ' with no animations');
|
||||
}, 'getAnimations for non-animated content');
|
||||
@@ -34,17 +34,17 @@ test(function(t) {
|
||||
|
||||
// Add an animation
|
||||
div.style.animation = 'animLeft 100s';
|
||||
assert_equals(document.timeline.getAnimations().length, 1,
|
||||
assert_equals(document.getAnimations().length, 1,
|
||||
'getAnimations returns a running CSS Animation');
|
||||
|
||||
// Add another animation
|
||||
div.style.animation = 'animLeft 100s, animTop 100s';
|
||||
assert_equals(document.timeline.getAnimations().length, 2,
|
||||
assert_equals(document.getAnimations().length, 2,
|
||||
'getAnimations returns two running CSS Animations');
|
||||
|
||||
// Remove both
|
||||
div.style.animation = '';
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'getAnimations returns no running CSS Animations');
|
||||
}, 'getAnimations for CSS Animations');
|
||||
|
||||
@@ -53,7 +53,7 @@ test(function(t) {
|
||||
div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
|
||||
'animBottom 100s';
|
||||
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, 4,
|
||||
'getAnimations returns all running CSS Animations');
|
||||
assert_equals(animations[0].animationName, 'animLeft',
|
||||
@@ -72,7 +72,7 @@ test(function(t) {
|
||||
var div3 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||
var div4 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, 4,
|
||||
'getAnimations returns all running CSS Animations');
|
||||
assert_equals(animations[0].effect.target, div1,
|
||||
@@ -95,7 +95,7 @@ test(function(t) {
|
||||
// Which should give: 2, 1, 4, 3
|
||||
div2.appendChild(div1);
|
||||
div2.appendChild(div4);
|
||||
animations = document.timeline.getAnimations();
|
||||
animations = document.getAnimations();
|
||||
assert_equals(animations[0].effect.target, div2,
|
||||
'Order of first animation returned after tree surgery');
|
||||
assert_equals(animations[1].effect.target, div1,
|
||||
@@ -114,7 +114,7 @@ test(function(t) {
|
||||
var expectedResults = [ [ div1, 'animLeft' ],
|
||||
[ div1, 'animTop' ],
|
||||
[ div2, 'animBottom' ] ];
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, expectedResults.length,
|
||||
'getAnimations returns all running CSS Animations');
|
||||
animations.forEach(function(anim, i) {
|
||||
@@ -132,7 +132,7 @@ test(function(t) {
|
||||
[ div1, 'animLeft' ],
|
||||
[ div1, 'animRight' ],
|
||||
[ div1, 'animTop' ] ];
|
||||
animations = document.timeline.getAnimations();
|
||||
animations = document.getAnimations();
|
||||
assert_equals(animations.length, expectedResults.length,
|
||||
'getAnimations returns all running CSS Animations after ' +
|
||||
'making changes');
|
||||
@@ -145,10 +145,8 @@ test(function(t) {
|
||||
}, 'Order of CSS Animations - across and within elements');
|
||||
|
||||
test(function(t) {
|
||||
cancelAllAnimationsOnEnd(t);
|
||||
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
|
||||
var animLeft = document.timeline.getAnimations()[0];
|
||||
var animLeft = document.getAnimations()[0];
|
||||
assert_equals(animLeft.animationName, 'animLeft',
|
||||
'Originally, animLeft animation comes first');
|
||||
|
||||
@@ -156,7 +154,7 @@ test(function(t) {
|
||||
div.style.animation = 'animTop 100s';
|
||||
animLeft.play();
|
||||
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns markup-bound and free animations');
|
||||
assert_equals(animations[0].animationName, 'animTop',
|
||||
@@ -165,18 +163,16 @@ test(function(t) {
|
||||
}, 'Order of CSS Animations - markup-bound vs free animations');
|
||||
|
||||
test(function(t) {
|
||||
cancelAllAnimationsOnEnd(t);
|
||||
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
|
||||
var animLeft = document.timeline.getAnimations()[0];
|
||||
var animTop = document.timeline.getAnimations()[1];
|
||||
var animLeft = document.getAnimations()[0];
|
||||
var animTop = document.getAnimations()[1];
|
||||
|
||||
// Disassociate both animations from markup and restart in opposite order
|
||||
div.style.animation = '';
|
||||
animTop.play();
|
||||
animLeft.play();
|
||||
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'getAnimations returns free animations');
|
||||
assert_equals(animations[0], animTop,
|
||||
@@ -187,7 +183,7 @@ test(function(t) {
|
||||
// Restarting an animation should have no effect
|
||||
animTop.cancel();
|
||||
animTop.play();
|
||||
assert_equals(document.timeline.getAnimations()[0], animTop,
|
||||
assert_equals(document.getAnimations()[0], animTop,
|
||||
'After restarting, the ordering of free animations' +
|
||||
' does not change');
|
||||
}, 'Order of CSS Animations - free animations');
|
||||
@@ -204,7 +200,7 @@ test(function(t) {
|
||||
flushComputedStyle(div);
|
||||
|
||||
// Although the transition was added later, it should come first in the list
|
||||
var animations = document.timeline.getAnimations();
|
||||
var animations = document.getAnimations();
|
||||
assert_equals(animations.length, 2,
|
||||
'Both CSS animations and transitions are returned');
|
||||
assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
|
||||
@@ -214,38 +210,36 @@ test(function(t) {
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
|
||||
div.getAnimations()[0].finish();
|
||||
assert_equals(document.timeline.getAnimations().length, 1,
|
||||
assert_equals(document.getAnimations().length, 1,
|
||||
'Forwards-filling CSS animations are returned');
|
||||
}, 'Finished but filling CSS Animations are returned');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||
div.getAnimations()[0].finish();
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'Non-filling finished CSS animations are not returned');
|
||||
}, 'Finished but not filling CSS Animations are not returned');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
|
||||
assert_equals(document.timeline.getAnimations().length, 1,
|
||||
assert_equals(document.getAnimations().length, 1,
|
||||
'Yet-to-start CSS animations are returned');
|
||||
}, 'Yet-to-start CSS Animations are returned');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||
div.getAnimations()[0].cancel();
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'CSS animations cancelled by the API are not returned');
|
||||
}, 'CSS Animations cancelled via the API are not returned');
|
||||
|
||||
test(function(t) {
|
||||
cancelAllAnimationsOnEnd(t);
|
||||
|
||||
var div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||
var anim = div.getAnimations()[0];
|
||||
anim.cancel();
|
||||
anim.play();
|
||||
assert_equals(document.timeline.getAnimations().length, 1,
|
||||
assert_equals(document.getAnimations().length, 1,
|
||||
'CSS animations cancelled and restarted by the API are ' +
|
||||
'returned');
|
||||
}, 'CSS Animations cancelled and restarted via the API are returned');
|
||||
@@ -256,7 +250,7 @@ test(function(t) {
|
||||
// but we haven't implemented a PseudoElement interface to use as
|
||||
// animation's target (bug 1174575) so we simply don't return these animations
|
||||
// until we can support them properly.
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'CSS animations on pseudo elements are not returned');
|
||||
}, 'CSS Animations targetting pseudo-elements are not returned');
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_timeline-get-animations.html");
|
||||
window.open("file_animation-id.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
+1
-1
@@ -9,7 +9,7 @@ setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_timeline-get-animations.html");
|
||||
window.open("file_document-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
+4
-4
@@ -6,7 +6,7 @@
|
||||
'use strict';
|
||||
|
||||
test(function(t) {
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'getAnimations returns an empty sequence for a document'
|
||||
+ ' with no animations');
|
||||
}, 'getAnimations for non-animated content');
|
||||
@@ -22,12 +22,12 @@ test(function(t) {
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.left = '100px';
|
||||
div.style.top = '100px';
|
||||
assert_equals(document.timeline.getAnimations().length, 2,
|
||||
assert_equals(document.getAnimations().length, 2,
|
||||
'getAnimations returns two running CSS Transitions');
|
||||
|
||||
// Remove both
|
||||
div.style.transitionProperty = 'none';
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'getAnimations returns no running CSS Transitions');
|
||||
}, 'getAnimations for CSS Transitions');
|
||||
|
||||
@@ -39,7 +39,7 @@ async_test(function(t) {
|
||||
var animations = div.getAnimations();
|
||||
assert_equals(animations.length, 1, 'Got transition');
|
||||
animations[0].finished.then(t.step_func(function() {
|
||||
assert_equals(document.timeline.getAnimations().length, 0,
|
||||
assert_equals(document.getAnimations().length, 0,
|
||||
'No animations returned');
|
||||
t.done();
|
||||
}));
|
||||
@@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
setup({explicit_done: true});
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{ "set": [["dom.animations-api.core.enabled", true]]},
|
||||
function() {
|
||||
window.open("file_document-get-animations.html");
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
@@ -1,6 +1,9 @@
|
||||
[DEFAULT]
|
||||
# Support files for chrome tests that we want to load over HTTP need
|
||||
# to go in here, not chrome.ini.
|
||||
support-files =
|
||||
testcommon.js
|
||||
chrome/file_animate_xrays.html
|
||||
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
support-files = css-animations/file_animations-dynamic-changes.html
|
||||
@@ -14,6 +17,8 @@ support-files = css-animations/file_animation-currenttime.html
|
||||
support-files = css-animations/file_animation-finish.html
|
||||
[css-animations/test_animation-finished.html]
|
||||
support-files = css-animations/file_animation-finished.html
|
||||
[css-animations/test_animation-id.html]
|
||||
support-files = css-animations/file_animation-id.html
|
||||
[css-animations/test_animation-oncancel.html]
|
||||
support-files = css-animations/file_animation-oncancel.html
|
||||
[css-animations/test_animation-onfinish.html]
|
||||
@@ -32,15 +37,15 @@ support-files = css-animations/file_animation-reverse.html
|
||||
support-files = css-animations/file_animation-starttime.html
|
||||
[css-animations/test_cssanimation-animationname.html]
|
||||
support-files = css-animations/file_cssanimation-animationname.html
|
||||
[css-animations/test_keyframeeffect-getframes.html]
|
||||
support-files = css-animations/file_keyframeeffect-getframes.html
|
||||
[css-animations/test_document-get-animations.html]
|
||||
support-files = css-animations/file_document-get-animations.html
|
||||
[css-animations/test_effect-target.html]
|
||||
support-files = css-animations/file_effect-target.html
|
||||
[css-animations/test_element-get-animations.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
support-files = css-animations/file_element-get-animations.html
|
||||
[css-animations/test_timeline-get-animations.html]
|
||||
support-files = css-animations/file_timeline-get-animations.html
|
||||
[css-animations/test_keyframeeffect-getframes.html]
|
||||
support-files = css-animations/file_keyframeeffect-getframes.html
|
||||
[css-transitions/test_animation-cancel.html]
|
||||
support-files = css-transitions/file_animation-cancel.html
|
||||
[css-transitions/test_animation-computed-timing.html]
|
||||
@@ -57,15 +62,15 @@ support-files = css-transitions/file_animation-ready.html
|
||||
support-files = css-transitions/file_animation-starttime.html
|
||||
[css-transitions/test_csstransition-transitionproperty.html]
|
||||
support-files = css-transitions/file_csstransition-transitionproperty.html
|
||||
[css-transitions/test_keyframeeffect-getframes.html]
|
||||
support-files = css-transitions/file_keyframeeffect-getframes.html
|
||||
[css-transitions/test_document-get-animations.html]
|
||||
support-files = css-transitions/file_document-get-animations.html
|
||||
[css-transitions/test_effect-target.html]
|
||||
support-files = css-transitions/file_effect-target.html
|
||||
[css-transitions/test_element-get-animations.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
support-files = css-transitions/file_element-get-animations.html
|
||||
[css-transitions/test_timeline-get-animations.html]
|
||||
support-files = css-transitions/file_timeline-get-animations.html
|
||||
[css-transitions/test_keyframeeffect-getframes.html]
|
||||
support-files = css-transitions/file_keyframeeffect-getframes.html
|
||||
[document-timeline/test_document-timeline.html]
|
||||
support-files = document-timeline/file_document-timeline.html
|
||||
[document-timeline/test_request_animation_frame.html]
|
||||
|
||||
@@ -17,18 +17,6 @@ function addDiv(t) {
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Some tests cause animations to continue to exist even after their target
|
||||
* element has been removed from the document tree. To ensure that these
|
||||
* animations do not impact other tests we should cancel them when the test
|
||||
* is complete.
|
||||
*/
|
||||
function cancelAllAnimationsOnEnd(t) {
|
||||
t.add_cleanup(function() {
|
||||
document.timeline.getAnimations().forEach(animation => animation.cancel());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise wrapper for requestAnimationFrame.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user