Merge remote-tracking branch 'origin/master' into kmeleon76

This commit is contained in:
2023-11-10 17:23:05 +08:00
1185 changed files with 22402 additions and 9326 deletions
+4 -4
View File
@@ -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;
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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);
}
+32
View File
@@ -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
+17
View File
@@ -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.
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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);
}
+3 -3
View File
@@ -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;
+6 -8
View File
@@ -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
+3 -3
View File
@@ -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;
};
+1 -1
View File
@@ -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) {
+28
View File
@@ -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);
}
+11
View File
@@ -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
+3 -21
View File
@@ -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
+9 -7
View File
@@ -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;
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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);
+2 -2
View File
@@ -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);
+4 -4
View File
@@ -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) {
+2
View File
@@ -267,6 +267,8 @@ DocAccessibleParent::Destroy()
ProxyDestroyed(iter.Get()->mProxy);
iter.Remove();
}
DocManager::NotifyOfRemoteDocShutdown(this);
ProxyDestroyed(this);
if (mParentDoc)
mParentDoc->RemoveChildDoc(this);
+8 -2
View File
@@ -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) {
+1
View File
@@ -24,6 +24,7 @@ if CONFIG['ACCESSIBILITY']:
LOCAL_INCLUDES += [
'../base',
'../generic',
'../xpcom',
]
if CONFIG['MOZ_ENABLE_GTK']:
+3 -3
View File
@@ -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);
}
+2 -2
View File
@@ -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);
}
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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 {
+12 -2
View File
@@ -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);
+2
View File
@@ -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')
+2
View File
@@ -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;
+1 -1
View File
@@ -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());
}
+37 -3
View File
@@ -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
+1 -1
View File
@@ -33,7 +33,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(xpcAccessibleGeneric)
Accessible*
xpcAccessibleGeneric::ToInternalAccessible() const
{
return mIntl;
return mIntl.AsAccessible();
}
////////////////////////////////////////////////////////////////////////////////
+28 -7
View File
@@ -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
+1 -1
View File
@@ -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++)
+12 -2
View File
@@ -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;
+5 -1
View File
@@ -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;
+1 -1
View File
@@ -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();
+4 -4
View File
@@ -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();
+5 -1
View File
@@ -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;
+2 -2
View File
@@ -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);
+11 -1
View File
@@ -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;
+9 -17
View File
@@ -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()
{
-1
View File
@@ -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;
+4 -7
View File
@@ -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
-1
View File
@@ -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;
+1 -7
View File
@@ -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)
{
-2
View File
@@ -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;
+1 -8
View File
@@ -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*
-1
View File
@@ -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;
+6
View File
@@ -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) {
+3 -4
View File
@@ -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();
+3 -3
View File
@@ -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)">
+1
View File
@@ -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
+3 -3
View File
@@ -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));
+1 -1
View File
@@ -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);
+36 -1
View File
@@ -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;
}
+9 -1
View File
@@ -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
+4 -3
View File
@@ -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
-42
View File
@@ -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)
{
-3
View File
@@ -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.
+65
View File
@@ -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
+14
View File
@@ -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
+111
View File
@@ -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
+88
View File
@@ -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
+73
View File
@@ -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
+9 -1
View File
@@ -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
+1 -1
View File
@@ -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
View File
@@ -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);
+62 -56
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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>
@@ -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');
@@ -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>
@@ -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>
@@ -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>
+13 -8
View File
@@ -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]
-12
View File
@@ -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