diff --git a/accessible/base/ARIAMap.cpp b/accessible/base/ARIAMap.cpp index 960716e360..3a21725cb2 100644 --- a/accessible/base/ARIAMap.cpp +++ b/accessible/base/ARIAMap.cpp @@ -44,7 +44,7 @@ static const nsRoleMapEntry sWAIRoleMaps[] = eNoValue, eNoAction, eNoLiveAttr, - kGenericAccType, + eAlert, kNoReqStates }, { // alertdialog diff --git a/accessible/base/AccTypes.h b/accessible/base/AccTypes.h index aa0582adb5..4833e3f785 100644 --- a/accessible/base/AccTypes.h +++ b/accessible/base/AccTypes.h @@ -68,20 +68,21 @@ enum AccType { * type, the same accessible class can have several types. */ enum AccGenericType { - eAutoComplete = 1 << 0, - eAutoCompletePopup = 1 << 1, - eButton = 1 << 2, - eCombobox = 1 << 3, - eDocument = 1 << 4, - eHyperText = 1 << 5, - eLandmark = 1 << 6, - eList = 1 << 7, - eListControl = 1 << 8, - eMenuButton = 1 << 9, - eSelect = 1 << 10, - eTable = 1 << 11, - eTableCell = 1 << 12, - eTableRow = 1 << 13, + eAlert = 1 << 0, + eAutoComplete = 1 << 1, + eAutoCompletePopup = 1 << 2, + eButton = 1 << 3, + eCombobox = 1 << 4, + eDocument = 1 << 5, + eHyperText = 1 << 6, + eLandmark = 1 << 7, + eList = 1 << 8, + eListControl = 1 << 9, + eMenuButton = 1 << 10, + eSelect = 1 << 11, + eTable = 1 << 12, + eTableCell = 1 << 13, + eTableRow = 1 << 14, eLastAccGenericType = eTableRow }; diff --git a/accessible/base/Logging.cpp b/accessible/base/Logging.cpp index b8f112bef3..46dc4c9f86 100644 --- a/accessible/base/Logging.cpp +++ b/accessible/base/Logging.cpp @@ -45,14 +45,16 @@ static ModuleRep sModuleMap[] = { { "events", logging::eEvents }, { "platforms", logging::ePlatforms }, - { "stack", logging::eStack }, { "text", logging::eText }, { "tree", logging::eTree }, { "DOMEvents", logging::eDOMEvents }, { "focus", logging::eFocus }, { "selection", logging::eSelection }, - { "notifications", logging::eNotifications } + { "notifications", logging::eNotifications }, + + { "stack", logging::eStack }, + { "verbose", logging::eVerbose } }; static void @@ -610,6 +612,61 @@ logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument, Stack(); } +void +logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...) +{ + if (IsEnabledAll(logging::eTree | aExtraFlags)) { + MsgBegin("TREE", aMsg); + + va_list vl; + va_start(vl, aExtraFlags); + const char* descr = nullptr; + while ((descr = va_arg(vl, const char*))) { + AccessibleInfo(descr, va_arg(vl, Accessible*)); + } + va_end(vl); + + MsgEnd(); + + if (aExtraFlags & eStack) { + Stack(); + } + } +} + +void +logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, + const char* aMsg1, Accessible* aAcc, + const char* aMsg2, nsINode* aNode) +{ + if (IsEnabledAll(logging::eTree | logging::eVerbose)) { + MsgBegin("TREE", aMsg); + AccessibleInfo(aMsg1, aAcc); + Accessible* acc = aAcc->Document()->GetAccessible(aNode); + if (acc) { + AccessibleInfo(aMsg2, acc); + } + else { + Node(aMsg2, aNode); + } + MsgEnd(); + } +} + + +void +logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent) +{ + if (IsEnabledAll(logging::eTree | aExtraFlags)) { + MsgBegin("TREE", aMsg); + AccessibleInfo("container", aParent); + for (uint32_t idx = 0; idx < aParent->ChildCount(); idx++) { + AccessibleInfo("child", aParent->GetChildAt(idx)); + } + MsgEnd(); + } +} + void logging::MsgBegin(const char* aTitle, const char* aMsgText, ...) { @@ -740,6 +797,62 @@ logging::Document(DocAccessible* aDocument) printf("\n"); } +void +logging::AccessibleInfo(const char* aDescr, Accessible* aAccessible) +{ + printf(" %s: %p; ", aDescr, static_cast(aAccessible)); + if (!aAccessible) { + printf("\n"); + return; + } + if (aAccessible->IsDefunct()) { + printf("defunct\n"); + return; + } + if (!aAccessible->Document() || aAccessible->Document()->IsDefunct()) { + printf("document is shutting down, no info\n"); + return; + } + + nsAutoString role; + GetAccService()->GetStringRole(aAccessible->Role(), role); + printf("role: %s", NS_ConvertUTF16toUTF8(role).get()); + + nsAutoString name; + aAccessible->Name(name); + if (!name.IsEmpty()) { + printf(", name: '%s'", NS_ConvertUTF16toUTF8(name).get()); + } + + printf(", idx: %d", aAccessible->IndexInParent()); + + nsINode* node = aAccessible->GetNode(); + if (!node) { + printf(", node: null\n"); + } + else if (node->IsNodeOfType(nsINode::eDOCUMENT)) { + printf(", document node: %p\n", static_cast(node)); + } + else if (node->IsNodeOfType(nsINode::eTEXT)) { + printf(", text node: %p\n", static_cast(node)); + } + else if (node->IsElement()) { + dom::Element* el = node->AsElement(); + + nsAutoCString tag; + el->NodeInfo()->NameAtom()->ToUTF8String(tag); + + nsIAtom* idAtom = el->GetID(); + nsAutoCString id; + if (idAtom) { + idAtom->ToUTF8String(id); + } + + printf(", element node: %p, %s@id='%s'\n", + static_cast(el), tag.get(), id.get()); + } +} + void logging::AccessibleNNode(const char* aDescr, Accessible* aAccessible) { @@ -818,6 +931,12 @@ logging::IsEnabled(uint32_t aModules) return sModules & aModules; } +bool +logging::IsEnabledAll(uint32_t aModules) +{ + return (sModules & aModules) == aModules; +} + bool logging::IsEnabled(const nsAString& aModuleStr) { diff --git a/accessible/base/Logging.h b/accessible/base/Logging.h index 09fc7767ee..43d0ed63c2 100644 --- a/accessible/base/Logging.h +++ b/accessible/base/Logging.h @@ -35,14 +35,17 @@ enum EModules { eEvents = 1 << 3, ePlatforms = 1 << 4, - eStack = 1 << 5, - eText = 1 << 6, - eTree = 1 << 7, + eText = 1 << 5, + eTree = 1 << 6, - eDOMEvents = 1 << 8, - eFocus = 1 << 9, - eSelection = 1 << 10, - eNotifications = eDOMEvents | eSelection | eFocus + eDOMEvents = 1 << 7, + eFocus = 1 << 8, + eSelection = 1 << 9, + eNotifications = eDOMEvents | eSelection | eFocus, + + // extras + eStack = 1 << 10, + eVerbose = 1 << 11 }; /** @@ -50,6 +53,11 @@ enum EModules { */ bool IsEnabled(uint32_t aModules); +/** + * Return true if all of the given modules are logged. + */ +bool IsEnabledAll(uint32_t aModules); + /** * Return true if the given module is logged. */ @@ -121,6 +129,15 @@ void FocusDispatched(Accessible* aTarget); void SelChange(nsISelection* aSelection, DocAccessible* aDocument, int16_t aReason); +/** + * Log the given accessible elements info. + */ +void TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...); +void TreeInfo(const char* aMsg, uint32_t aExtraFlags, + const char* aMsg1, Accessible* aAcc, + const char* aMsg2, nsINode* aNode); +void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent); + /** * Log the message ('title: text' format) on new line. Print the start and end * boundaries of the message body designated by '{' and '}' (2 spaces indent for @@ -164,6 +181,7 @@ void Document(DocAccessible* aDocument); /** * Log the accessible and its DOM node as a message entry. */ +void AccessibleInfo(const char* aDescr, Accessible* aAccessible); void AccessibleNNode(const char* aDescr, Accessible* aAccessible); void AccessibleNNode(const char* aDescr, nsINode* aNode); @@ -189,7 +207,8 @@ void Enable(const nsAFlatCString& aModules); */ void CheckEnv(); -} // namespace logs +} // namespace logging + } // namespace a11y } // namespace mozilla diff --git a/accessible/base/TreeWalker.cpp b/accessible/base/TreeWalker.cpp index de3d155469..79ad5c9269 100644 --- a/accessible/base/TreeWalker.cpp +++ b/accessible/base/TreeWalker.cpp @@ -21,18 +21,33 @@ using namespace mozilla::a11y; //////////////////////////////////////////////////////////////////////////////// TreeWalker:: - TreeWalker(Accessible* aContext, nsIContent* aContent, uint32_t aFlags) : - mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aContent), - mFlags(aFlags) + TreeWalker(Accessible* aContext) : + mDoc(aContext->Document()), mContext(aContext), mAnchorNode(nullptr), + mARIAOwnsIdx(0), + mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(0), + mPhase(eAtStart) { - NS_ASSERTION(aContent, "No node for the accessible tree walker!"); - - mChildFilter = mContext->NoXBLKids() ? + mChildFilter |= mContext->NoXBLKids() ? nsIContent::eAllButXBL : nsIContent::eAllChildren; - mChildFilter |= nsIContent::eSkipPlaceholderContent; - if (aContent) - PushState(aContent); + mAnchorNode = mContext->IsDoc() ? + mDoc->DocumentNode()->GetRootElement() : mContext->GetContent(); + + MOZ_COUNT_CTOR(TreeWalker); +} + +TreeWalker:: + TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags) : + mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aAnchorNode), + mARIAOwnsIdx(0), + mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(aFlags), + mPhase(eAtStart) +{ + MOZ_ASSERT(mFlags & eWalkCache, "This constructor cannot be used for tree creation"); + MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker"); + + mChildFilter |= mContext->NoXBLKids() ? + nsIContent::eAllButXBL : nsIContent::eAllChildren; MOZ_COUNT_CTOR(TreeWalker); } @@ -42,35 +57,122 @@ TreeWalker::~TreeWalker() MOZ_COUNT_DTOR(TreeWalker); } -//////////////////////////////////////////////////////////////////////////////// -// TreeWalker: private - -Accessible* -TreeWalker::Next() +bool +TreeWalker::Seek(nsIContent* aChildNode) { - if (mStateStack.IsEmpty()) - return nullptr; + MOZ_ASSERT(aChildNode, "Child cannot be null"); - ChildrenIterator* top = &mStateStack[mStateStack.Length() - 1]; - while (top) { - Accessible* child = nullptr; - bool skipSubtree = false; - while (nsIContent* childNode = Next(top, &child, &skipSubtree)) { - if (child) - return child; + mPhase = eAtStart; + mStateStack.Clear(); + mARIAOwnsIdx = 0; - // Walk down into subtree to find accessibles. - if (!skipSubtree && childNode->IsElement()) - top = PushState(childNode); + nsIContent* childNode = nullptr; + nsINode* parentNode = aChildNode; + do { + childNode = parentNode->AsContent(); + parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) && + (mChildFilter & nsIContent::eAllButXBL) ? + childNode->GetParentNode() : childNode->GetFlattenedTreeParent(); + + if (!parentNode || !parentNode->IsElement()) { + return false; } + // If ARIA owned child. + Accessible* child = mDoc->GetAccessible(childNode); + if (child && child->IsRelocated()) { + if (child->Parent() != mContext) { + return false; + } + + Accessible* ownedChild = nullptr; + while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) && + ownedChild != child); + + MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements"); + mPhase = eAtARIAOwns; + return true; + } + + // Look in DOM. + dom::AllChildrenIterator* iter = PrependState(parentNode->AsElement(), true); + if (!iter->Seek(childNode)) { + return false; + } + + if (parentNode == mAnchorNode) { + mPhase = eAtDOM; + return true; + } + } while (true); + + return false; +} + +Accessible* +TreeWalker::Next(nsIContent* aStopNode) +{ + if (mStateStack.IsEmpty()) { + if (mPhase == eAtEnd) { + return nullptr; + } + + if (mPhase == eAtDOM || mPhase == eAtARIAOwns) { + mPhase = eAtARIAOwns; + Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx); + if (child) { + mARIAOwnsIdx++; + return child; + } + mPhase = eAtEnd; + return nullptr; + } + + if (!mAnchorNode) { + mPhase = eAtEnd; + return nullptr; + } + + mPhase = eAtDOM; + PushState(mAnchorNode, true); + } + + dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1]; + while (top) { + if (aStopNode && top->Get() == aStopNode) { + return nullptr; + } + + while (nsIContent* childNode = top->GetNextChild()) { + bool skipSubtree = false; + Accessible* child = AccessibleFor(childNode, mFlags, &skipSubtree); + if (child) { + return child; + } + + // Walk down the subtree if allowed, otherwise check if we have reached + // a stop node. + if (!skipSubtree && childNode->IsElement()) { + top = PushState(childNode, true); + } + else if (childNode == aStopNode) { + return nullptr; + } + } top = PopState(); } // If we traversed the whole subtree of the anchor node. Move to next node - // relative anchor node within the context subtree if possible. - if (mFlags != eWalkContextTree) - return nullptr; + // relative anchor node within the context subtree if asked. + if (mFlags != eWalkContextTree) { + // eWalkCache flag presence indicates that the search is scoped to the + // anchor (no ARIA owns stuff). + if (mFlags & eWalkCache) { + mPhase = eAtEnd; + return nullptr; + } + return Next(aStopNode); + } nsINode* contextNode = mContext->GetNode(); while (mAnchorNode != contextNode) { @@ -79,10 +181,10 @@ TreeWalker::Next() return nullptr; nsIContent* parent = parentNode->AsElement(); - top = PushState(parent); - if (top->mDOMIter.Seek(mAnchorNode)) { + top = PushState(parent, true); + if (top->Seek(mAnchorNode)) { mAnchorNode = parent; - return Next(); + return Next(aStopNode); } // XXX We really should never get here, it means we're trying to find an @@ -92,58 +194,110 @@ TreeWalker::Next() mAnchorNode = parent; } - return nullptr; + return Next(aStopNode); } -nsIContent* -TreeWalker::Next(ChildrenIterator* aIter, Accessible** aAccesible, - bool* aSkipSubtree) +Accessible* +TreeWalker::Prev() { - nsIContent* childEl = aIter->mDOMIter.GetNextChild(); - if (!aAccesible) - return childEl; - - *aAccesible = nullptr; - *aSkipSubtree = false; - - if (childEl) { - Accessible* accessible = nullptr; - if (mFlags & eWalkCache) { - accessible = mDoc->GetAccessible(childEl); - } - else if (mContext->IsAcceptableChild(childEl)) { - accessible = GetAccService()-> - GetOrCreateAccessible(childEl, mContext, aSkipSubtree); + if (mStateStack.IsEmpty()) { + if (mPhase == eAtStart || mPhase == eAtDOM) { + mPhase = eAtStart; + return nullptr; } - // Ignore the accessible and its subtree if it was repositioned by means of - // aria-owns. - if (accessible) { - if (accessible->IsRelocated()) { - *aSkipSubtree = true; - } else { - *aAccesible = accessible; + if (mPhase == eAtEnd) { + mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext); + mPhase = eAtARIAOwns; + } + + if (mPhase == eAtARIAOwns) { + if (mARIAOwnsIdx > 0) { + return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx); + } + + if (!mAnchorNode) { + mPhase = eAtStart; + return nullptr; + } + + mPhase = eAtDOM; + PushState(mAnchorNode, false); + } + } + + dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1]; + while (top) { + while (nsIContent* childNode = top->GetPreviousChild()) { + // No accessible creation on the way back. + bool skipSubtree = false; + Accessible* child = AccessibleFor(childNode, eWalkCache, &skipSubtree); + if (child) { + return child; + } + + // Walk down into subtree to find accessibles. + if (!skipSubtree && childNode->IsElement()) { + top = PushState(childNode, false); } } - return childEl; + top = PopState(); } - // At last iterate over ARIA owned children. - Accessible* parent = mDoc->GetAccessible(aIter->mDOMIter.Parent()); - if (parent) { - Accessible* child = mDoc->ARIAOwnedAt(parent, aIter->mARIAOwnsIdx++); - if (child) { - *aAccesible = child; - return child->GetContent(); - } + // Move to a previous node relative the anchor node within the context + // subtree if asked. + if (mFlags != eWalkContextTree) { + mPhase = eAtStart; + return nullptr; } + + nsINode* contextNode = mContext->GetNode(); + while (mAnchorNode != contextNode) { + nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent(); + if (!parentNode || !parentNode->IsElement()) { + return nullptr; + } + + nsIContent* parent = parentNode->AsElement(); + top = PushState(parent, true); + if (top->Seek(mAnchorNode)) { + mAnchorNode = parent; + return Prev(); + } + + mAnchorNode = parent; + } + + mPhase = eAtStart; return nullptr; } -TreeWalker::ChildrenIterator* +Accessible* +TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags, bool* aSkipSubtree) +{ + Accessible* child = nullptr; + if (aFlags & eWalkCache) { + child = mDoc->GetAccessible(aNode); + } + else if (mContext->IsAcceptableChild(aNode)) { + child = GetAccService()-> + GetOrCreateAccessible(aNode, mContext, aSkipSubtree); + } + + // Ignore the accessible and its subtree if it was repositioned by means + // of aria-owns. + if (child && child->IsRelocated()) { + *aSkipSubtree = true; + return nullptr; + } + + return child; +} + +dom::AllChildrenIterator* TreeWalker::PopState() { size_t length = mStateStack.Length(); mStateStack.RemoveElementAt(length - 1); - return mStateStack.IsEmpty() ? nullptr : &mStateStack[mStateStack.Length() - 1]; + return mStateStack.IsEmpty() ? nullptr : &mStateStack.LastElement(); } diff --git a/accessible/base/TreeWalker.h b/accessible/base/TreeWalker.h index d35c8147ce..afce61789f 100644 --- a/accessible/base/TreeWalker.h +++ b/accessible/base/TreeWalker.h @@ -33,63 +33,94 @@ public: }; /** - * Constructor + * Used to navigate and create if needed the accessible children. + */ + explicit TreeWalker(Accessible* aContext); + + /** + * Used to navigate the accessible children relative to the anchor. * * @param aContext [in] container accessible for the given node, used to * define accessible context - * @param aNode [in] the node the search will be prepared relative to + * @param aAnchorNode [in] the node the search will be prepared relative to * @param aFlags [in] flags (see enum above) */ - TreeWalker(Accessible* aContext, nsIContent* aNode, uint32_t aFlags = 0); + TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags = eWalkCache); + ~TreeWalker(); /** - * Return the next accessible. + * Clears the tree walker state and resets it to the given child within + * the anchor. + */ + bool Seek(nsIContent* aChildNode); + + /** + * Return the next/prev 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* Next(); + Accessible* Next(nsIContent* aStopNode = nullptr); + Accessible* Prev(); + + Accessible* Context() const { return mContext; } + DocAccessible* Document() const { return mDoc; } private: TreeWalker(); TreeWalker(const TreeWalker&); TreeWalker& operator =(const TreeWalker&); - struct ChildrenIterator { - ChildrenIterator(nsIContent* aNode, uint32_t aFilter) : - mDOMIter(aNode, aFilter), mARIAOwnsIdx(0) { } - - dom::AllChildrenIterator mDOMIter; - uint32_t mARIAOwnsIdx; - }; - - nsIContent* Next(ChildrenIterator* aIter, Accessible** aAccessible = nullptr, - bool* aSkipSubtree = nullptr); + /** + * Return an accessible for the given node if any. + */ + Accessible* AccessibleFor(nsIContent* aNode, uint32_t aFlags, + bool* aSkipSubtree); /** - * Create new state for the given node and push it on top of stack. + * Create new state for the given node and push it on top of stack / at bottom + * of stack. * * @note State stack is used to navigate up/down the DOM subtree during * accessible children search. */ - ChildrenIterator* PushState(nsIContent* aContent) + dom::AllChildrenIterator* PushState(nsIContent* aContent, + bool aStartAtBeginning) { - return mStateStack.AppendElement(ChildrenIterator(aContent, mChildFilter)); + return mStateStack.AppendElement( + dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning)); + } + dom::AllChildrenIterator* PrependState(nsIContent* aContent, + bool aStartAtBeginning) + { + return mStateStack.InsertElementAt(0, + dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning)); } /** * Pop state from stack. */ - ChildrenIterator* PopState(); + dom::AllChildrenIterator* PopState(); DocAccessible* mDoc; Accessible* mContext; nsIContent* mAnchorNode; - AutoTArray mStateStack; + + AutoTArray mStateStack; + uint32_t mARIAOwnsIdx; + int32_t mChildFilter; uint32_t mFlags; + + enum Phase { + eAtStart, + eAtDOM, + eAtARIAOwns, + eAtEnd + }; + Phase mPhase; }; } // namespace a11y diff --git a/accessible/generic/Accessible.cpp b/accessible/generic/Accessible.cpp index 7bc1732c6f..2338221471 100644 --- a/accessible/generic/Accessible.cpp +++ b/accessible/generic/Accessible.cpp @@ -1983,6 +1983,10 @@ Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent) if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent)) SetARIAHidden(true); + + mContextFlags |= + static_cast((mParent->IsAlert() || + mParent->IsInsideAlert())) & eInsideAlert; } // Accessible protected @@ -2000,7 +2004,7 @@ Accessible::UnbindFromParent() delete mBits.groupInfo; mBits.groupInfo = nullptr; - mContextFlags &= ~eHasNameDependentParent; + mContextFlags &= ~eHasNameDependentParent & ~eInsideAlert; } //////////////////////////////////////////////////////////////////////////////// @@ -2542,10 +2546,9 @@ Accessible::LastRelease() void Accessible::CacheChildren() { - DocAccessible* doc = Document(); - NS_ENSURE_TRUE_VOID(doc); + NS_ENSURE_TRUE_VOID(Document()); - TreeWalker walker(this, mContent); + TreeWalker walker(this); Accessible* child = nullptr; while ((child = walker.Next()) && AppendChild(child)); diff --git a/accessible/generic/Accessible.h b/accessible/generic/Accessible.h index 5becdd2a64..f6992439d9 100644 --- a/accessible/generic/Accessible.h +++ b/accessible/generic/Accessible.h @@ -568,6 +568,8 @@ public: return mContent->IsAnyOfHTMLElements(nsGkAtoms::abbr, nsGkAtoms::acronym); } + bool IsAlert() const { return HasGenericType(eAlert); } + bool IsApplication() const { return mType == eApplicationType; } ApplicationAccessible* AsApplication(); @@ -946,6 +948,11 @@ public: bool IsARIAHidden() const { return mContextFlags & eARIAHidden; } void SetARIAHidden(bool aIsDefined); + /** + * Return true if the element is inside an alert. + */ + bool IsInsideAlert() const { return mContextFlags & eInsideAlert; } + protected: virtual ~Accessible(); @@ -1034,8 +1041,9 @@ protected: enum ContextFlags { eHasNameDependentParent = 1 << 0, // Parent's name depends on this accessible. eARIAHidden = 1 << 1, + eInsideAlert = 1 << 2, - eLastContextFlag = eARIAHidden + eLastContextFlag = eInsideAlert }; protected: @@ -1141,9 +1149,9 @@ protected: static const uint8_t kChildrenFlagsBits = 2; static const uint8_t kStateFlagsBits = 11; - static const uint8_t kContextFlagsBits = 2; + static const uint8_t kContextFlagsBits = 3; static const uint8_t kTypeBits = 6; - static const uint8_t kGenericTypesBits = 14; + static const uint8_t kGenericTypesBits = 15; /** * Keep in sync with ChildrenFlags, StateFlags, ContextFlags, and AccTypes. diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index d20d1844a4..5b56e411a4 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -1421,36 +1421,6 @@ if (!aNode->IsContent() || !aNode->AsContent()->IsHTMLElement(nsGkAtoms::area)) return GetAccessible(aNode); } -//////////////////////////////////////////////////////////////////////////////// -// Accessible protected - -void -DocAccessible::CacheChildren() -{ - // Search for accessible children starting from the document element since - // some web pages tend to insert elements under it rather than document body. - dom::Element* rootElm = mDocumentNode->GetRootElement(); - if (!rootElm) - return; - - // Ignore last HTML:br, copied from HyperTextAccessible. - TreeWalker walker(this, rootElm); - Accessible* lastChild = nullptr; - while (Accessible* child = walker.Next()) { - if (lastChild) - AppendChild(lastChild); - - lastChild = child; - } - - if (lastChild) { - if (lastChild->IsHTMLBr()) - Document()->UnbindFromDocument(lastChild); - else - AppendChild(lastChild); - } -} - //////////////////////////////////////////////////////////////////////////////// // Protected members @@ -1786,22 +1756,16 @@ DocAccessible::UpdateTreeOnInsertion(Accessible* aContainer) // Check to see if change occurred inside an alert, and fire an EVENT_ALERT // if it did. - if (!(updateFlags & eAlertAccessible)) { - // XXX: tree traversal is perf issue, accessible should know if they are - // children of alert accessible to avoid this. + if (!(updateFlags & eAlertAccessible) && + (aContainer->IsAlert() || aContainer->IsInsideAlert())) { Accessible* ancestor = aContainer; - while (ancestor) { - if (ancestor->ARIARole() == roles::ALERT) { + do { + if (ancestor->IsAlert()) { FireDelayedEvent(nsIAccessibleEvent::EVENT_ALERT, ancestor); break; } - - // Don't climb above this document. - if (ancestor == this) - break; - - ancestor = ancestor->Parent(); } + while ((ancestor = ancestor->Parent())); } MaybeNotifyOfValueChange(aContainer); @@ -1834,34 +1798,9 @@ DocAccessible::UpdateTreeOnRemoval(Accessible* aContainer, nsIContent* aChildNod if (child) { updateFlags |= UpdateTreeInternal(child, false, reorderEvent); } else { - // aChildNode may not coorespond to a particular accessible, to handle - // this we go through all the children of aContainer. Then if a child - // has aChildNode as an ancestor, or does not have the node for - // aContainer as an ancestor remove that child of aContainer. Note that - // when we are called aChildNode may already have been removed from the DOM - // so we can't expect it to have a parent or what was it's parent to have - // it as a child. - nsINode* containerNode = aContainer->GetNode(); - for (uint32_t idx = 0; idx < aContainer->ContentChildCount();) { - Accessible* child = aContainer->ContentChildAt(idx); - - // If accessible doesn't have its own content then we assume parent - // will handle its update. If child is DocAccessible then we don't - // handle updating it here either. - if (!child->HasOwnContent() || child->IsDoc()) { - idx++; - continue; - } - - nsINode* childNode = child->GetContent(); - while (childNode != aChildNode && childNode != containerNode && - (childNode = childNode->GetParentNode())); - - if (childNode != containerNode) { - updateFlags |= UpdateTreeInternal(child, false, reorderEvent); - } else { - idx++; - } + TreeWalker walker(aContainer, aChildNode, TreeWalker::eWalkCache); + while (Accessible* child = walker.Next()) { + updateFlags |= UpdateTreeInternal(child, false, reorderEvent); } } @@ -2057,6 +1996,7 @@ DocAccessible::DoARIAOwnsRelocation(Accessible* aOwner) MoveChild(child, insertIdx); children->InsertElementAt(arrayIdx, child); arrayIdx++; + insertIdx = child->IndexInParent() + 1; } else if (SeizeChild(aOwner, child, insertIdx)) { children->InsertElementAt(arrayIdx, child); @@ -2083,6 +2023,12 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild, int32_t oldIdxInParent = aChild->IndexInParent(); +#ifdef A11Y_LOG + logging::TreeInfo("aria owns seize child", 0, + "old parent", oldParent, "new parent", aNewParent, + "child", aChild, nullptr); +#endif + RefPtr reorderEvent = new AccReorderEvent(oldParent); RefPtr hideEvent = new AccHideEvent(aChild, false); reorderEvent->AddSubMutationEvent(hideEvent); @@ -2098,6 +2044,11 @@ DocAccessible::SeizeChild(Accessible* aNewParent, Accessible* aChild, isReinserted = aNewParent->InsertChildAt(aIdxInParent, aChild); } +#ifdef A11Y_LOG + logging::TreeInfo("aria owns seize child: new parent tree after", + logging::eVerbose, aNewParent); +#endif + if (!isReinserted) { AutoTreeMutation mut(oldParent); oldParent->InsertChildAt(oldIdxInParent, aChild); @@ -2136,10 +2087,20 @@ DocAccessible::MoveChild(Accessible* aChild, int32_t aIdxInParent) RefPtr hideEvent = new AccHideEvent(aChild, false); reorderEvent->AddSubMutationEvent(hideEvent); +#ifdef A11Y_LOG + logging::TreeInfo("aria owns move child", 0, + "parent", parent, "child", aChild, nullptr); +#endif + AutoTreeMutation mut(parent); parent->MoveChild(aIdxInParent, aChild); aChild->SetRelocated(true); +#ifdef A11Y_LOG + logging::TreeInfo("aria owns move child: parent tree after", + logging::eVerbose, parent); +#endif + FireDelayedEvent(hideEvent); RefPtr showEvent = new AccShowEvent(aChild); diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 8a5e5ae90e..5bd62e4f12 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -291,6 +291,11 @@ public: } return nullptr; } + uint32_t ARIAOwnedCount(Accessible* aParent) const + { + nsTArray >* children = mARIAOwnsHash.Get(aParent); + return children ? children->Length() : 0; + } /** * Return true if the given ID is referred by relation attribute. @@ -359,9 +364,6 @@ protected: void LastRelease(); - // Accessible - virtual void CacheChildren() override; - // DocAccessible virtual nsresult AddEventListeners(); virtual nsresult RemoveEventListeners(); diff --git a/accessible/generic/HyperTextAccessible.cpp b/accessible/generic/HyperTextAccessible.cpp index 751211668a..e6bc7e0830 100644 --- a/accessible/generic/HyperTextAccessible.cpp +++ b/accessible/generic/HyperTextAccessible.cpp @@ -1935,31 +1935,6 @@ HyperTextAccessible::RelationByType(RelationType aType) return rel; } -void -HyperTextAccessible::CacheChildren() -{ - // Trailing HTML br element don't play any difference. We don't need to expose - // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16 - // for details). - - TreeWalker walker(this, mContent); - Accessible* child = nullptr; - Accessible* lastChild = nullptr; - while ((child = walker.Next())) { - if (lastChild) - AppendChild(lastChild); - - lastChild = child; - } - - if (lastChild) { - if (lastChild->IsHTMLBr()) - Document()->UnbindFromDocument(lastChild); - else - AppendChild(lastChild); - } -} - //////////////////////////////////////////////////////////////////////////////// // HyperTextAccessible public static diff --git a/accessible/generic/HyperTextAccessible.h b/accessible/generic/HyperTextAccessible.h index 4d6e39f608..25fcfb8154 100644 --- a/accessible/generic/HyperTextAccessible.h +++ b/accessible/generic/HyperTextAccessible.h @@ -434,7 +434,6 @@ protected: // Accessible virtual ENameValueFlag NativeName(nsString& aName) override; - virtual void CacheChildren() override; // HyperTextAccessible diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 842af97d03..0c591e5003 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -126,9 +126,23 @@ function dumpTree(aId, aMsg) } } + function dumpDOMTreeIntl(node, indent) + { + dump(indent + prettyName(node) + "\n"); + + var children = node.childNodes; + for (var i = 0; i < children.length; i++) { + var child = children.item(i); + dumpDOMTreeIntl(child, indent + " "); + } + } + dump(aMsg + "\n"); var root = getAccessible(aId); dumpTreeIntl(root, " "); + + dump("DOM tree:\n"); + dumpDOMTreeIntl(getNode(aId), " "); } /** diff --git a/accessible/tests/mochitest/editabletext/editabletext.js b/accessible/tests/mochitest/editabletext/editabletext.js index ce2f9ebd27..5c4470782b 100644 --- a/accessible/tests/mochitest/editabletext/editabletext.js +++ b/accessible/tests/mochitest/editabletext/editabletext.js @@ -62,6 +62,7 @@ function editableTextTest(aID) function setTextContentsInvoke() { + dump(`\nsetTextContents '${aValue}'\n`); var acc = getAccessible(aID, nsIAccessibleEditableText); acc.setTextContents(aValue); } @@ -84,6 +85,7 @@ function editableTextTest(aID) function insertTextInvoke() { + dump(`\ninsertText '${aStr}' at ${aPos} pos\n`); var acc = getAccessible(aID, nsIAccessibleEditableText); acc.insertText(aStr, aPos); } diff --git a/accessible/tests/mochitest/events/test_aria_menu.html b/accessible/tests/mochitest/events/test_aria_menu.html index d6b99b9376..2dd00478e5 100644 --- a/accessible/tests/mochitest/events/test_aria_menu.html +++ b/accessible/tests/mochitest/events/test_aria_menu.html @@ -153,6 +153,7 @@ var gQueue = null; //gA11yEventDumpToConsole = true; // debuging + //enableLogging("tree,verbose"); function doTests() { diff --git a/accessible/tests/mochitest/treeupdate/test_ariaowns.html b/accessible/tests/mochitest/treeupdate/test_ariaowns.html index 2070136f1b..abe88c3841 100644 --- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html +++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html @@ -441,12 +441,81 @@ } } + function rearrangeARIAOwns(aContainer, aAttr, aIdList, aRoleList) + { + this.eventSeq = []; + for (var id of aIdList) { + this.eventSeq.push(new invokerChecker(EVENT_HIDE, getNode(id))); + this.eventSeq.push(new invokerChecker(EVENT_SHOW, getNode(id))); + } + this.eventSeq.push(new invokerChecker(EVENT_REORDER, getNode(aContainer))); + + this.invoke = function rearrangeARIAOwns_invoke() + { + getNode(aContainer).setAttribute("aria-owns", aAttr); + } + + this.finalCheck = function rearrangeARIAOwns_finalCheck() + { + var tree = { SECTION: [ ] }; + for (var role of aRoleList) { + var ch = {}; + ch[role] = []; + tree["SECTION"].push(ch); + } + testAccessibleTree(aContainer, tree); + } + + this.getID = function rearrangeARIAOwns_getID() + { + return `Rearrange @aria-owns attribute to '${aAttr}'`; + } + } + + function removeNotARIAOwnedEl(aContainer, aChild) + { + this.eventSeq = [ + new invokerChecker(EVENT_REORDER, aContainer) + ]; + + this.invoke = function removeNotARIAOwnedEl_invoke() + { + dumpTree(aContainer, "before"); + var tree = { + SECTION: [ + { TEXT_LEAF: [ ] }, + { GROUPING: [ ] } + ] + }; + testAccessibleTree(aContainer, tree); + + getNode(aContainer).removeChild(getNode(aChild)); + } + + this.finalCheck = function removeNotARIAOwnedEl_finalCheck() + { + dumpTree(aContainer, "after"); + var tree = { + SECTION: [ + { GROUPING: [ ] } + ] + }; + testAccessibleTree(aContainer, tree); + } + + this.getID = function removeNotARIAOwnedEl_getID() + { + return `remove not ARIA owned child`; + } + } + + //////////////////////////////////////////////////////////////////////////// // Test //////////////////////////////////////////////////////////////////////////// //gA11yEventDumpToConsole = true; - //enableLogging("tree"); // debug stuff + //enableLogging("tree,verbose"); // debug stuff var gQueue = null; @@ -474,6 +543,18 @@ // test4 gQueue.push(new showHiddenElement()); + // test5 + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_checkbox t5_radio t5_button", + [ "t5_checkbox", "t5_radio", "t5_button" ], + [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ])); + gQueue.push(new rearrangeARIAOwns( + "t5_container", "t5_radio t5_button t5_checkbox", + [ "t5_radio", "t5_button" ], + [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ])); + + gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span")); + gQueue.invoke(); // SimpleTest.finish() will be called in the end } @@ -515,6 +596,17 @@ + +
+
+ + +
+ +
+ hey +
+
diff --git a/accessible/xul/XULAlertAccessible.cpp b/accessible/xul/XULAlertAccessible.cpp index 394904a733..4b1b5bd8d8 100644 --- a/accessible/xul/XULAlertAccessible.cpp +++ b/accessible/xul/XULAlertAccessible.cpp @@ -19,6 +19,7 @@ XULAlertAccessible:: XULAlertAccessible(nsIContent* aContent, DocAccessible* aDoc) : AccessibleWrap(aContent, aDoc) { + mGenericTypes |= eAlert; } XULAlertAccessible::~XULAlertAccessible() diff --git a/b2g/components/AboutServiceWorkers.jsm b/b2g/components/AboutServiceWorkers.jsm index 4c36bb1c63..1fd3e458d1 100644 --- a/b2g/components/AboutServiceWorkers.jsm +++ b/b2g/components/AboutServiceWorkers.jsm @@ -29,17 +29,12 @@ function serializeServiceWorkerInfo(aServiceWorkerInfo) { let result = {}; - Object.keys(aServiceWorkerInfo).forEach(property => { - if (typeof aServiceWorkerInfo[property] == "function") { - return; - } - if (property === "principal") { - result.principal = { - origin: aServiceWorkerInfo.principal.origin, - originAttributes: aServiceWorkerInfo.principal.originAttributes - }; - return; - } + result.principal = { + origin: aServiceWorkerInfo.principal.originNoSuffix, + originAttributes: aServiceWorkerInfo.principal.originAttributes + }; + + ["scope", "scriptSpec"].forEach(property => { result[property] = aServiceWorkerInfo[property]; }); diff --git a/b2g/components/ContentPermissionPrompt.js b/b2g/components/ContentPermissionPrompt.js index 138e8368b2..4edf7d3841 100644 --- a/b2g/components/ContentPermissionPrompt.js +++ b/b2g/components/ContentPermissionPrompt.js @@ -32,7 +32,7 @@ Cu.import("resource://gre/modules/PermissionsTable.jsm"); var permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager); var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); -let permissionSpecificChecker = {}; +var permissionSpecificChecker = {}; XPCOMUtils.defineLazyServiceGetter(this, "TelephonyService", @@ -413,7 +413,10 @@ ContentPermissionPrompt.prototype = { type: type, permissions: permissions, id: requestId, - origin: principal.origin, + // This system app uses the origin from permission events to + // compare against the mozApp.origin of app windows, so we + // are not concerned with origin suffixes here (appId, etc). + origin: principal.originNoSuffix, isApp: isApp, remember: remember, isGranted: isGranted, diff --git a/b2g/components/MailtoProtocolHandler.js b/b2g/components/MailtoProtocolHandler.js index 60b1bbaaff..3f667ab53d 100644 --- a/b2g/components/MailtoProtocolHandler.js +++ b/b2g/components/MailtoProtocolHandler.js @@ -23,7 +23,7 @@ MailtoProtocolHandler.prototype = { Ci.nsIProtocolHandler.URI_NOAUTH | Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE | Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA, - allowPort: function() false, + allowPort: () => false, newURI: function Proto_newURI(aSpec, aOriginCharset) { let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI); diff --git a/b2g/components/SimulatorScreen.js b/b2g/components/SimulatorScreen.js index dea86fc795..18d8f5cc46 100644 --- a/b2g/components/SimulatorScreen.js +++ b/b2g/components/SimulatorScreen.js @@ -62,13 +62,13 @@ function hookScreen(window) { }; Object.defineProperty(screen, 'width', { - get: function () GlobalSimulatorScreen.width + get: () => GlobalSimulatorScreen.width }); Object.defineProperty(screen, 'height', { - get: function () GlobalSimulatorScreen.height + get: () => GlobalSimulatorScreen.height }); Object.defineProperty(screen, 'mozOrientation', { - get: function () GlobalSimulatorScreen.mozOrientation + get: () => GlobalSimulatorScreen.mozOrientation }); } diff --git a/b2g/components/SmsProtocolHandler.js b/b2g/components/SmsProtocolHandler.js index cfefcff039..339f8605fa 100644 --- a/b2g/components/SmsProtocolHandler.js +++ b/b2g/components/SmsProtocolHandler.js @@ -32,7 +32,7 @@ SmsProtocolHandler.prototype = { Ci.nsIProtocolHandler.URI_NOAUTH | Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE | Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA, - allowPort: function() false, + allowPort: () => false, newURI: function Proto_newURI(aSpec, aOriginCharset) { let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI); diff --git a/b2g/components/TelProtocolHandler.js b/b2g/components/TelProtocolHandler.js index 38e0f2785c..bbc0e02193 100644 --- a/b2g/components/TelProtocolHandler.js +++ b/b2g/components/TelProtocolHandler.js @@ -31,7 +31,7 @@ TelProtocolHandler.prototype = { Ci.nsIProtocolHandler.URI_NOAUTH | Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE | Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA, - allowPort: function() false, + allowPort: () => false, newURI: function Proto_newURI(aSpec, aOriginCharset) { let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI); diff --git a/b2g/simulator/lib/simulator-process.js b/b2g/simulator/lib/simulator-process.js index bf20cd0b52..43181f4921 100644 --- a/b2g/simulator/lib/simulator-process.js +++ b/b2g/simulator/lib/simulator-process.js @@ -21,10 +21,10 @@ const { EventEmitter } = Cu.import("resource://gre/modules/devtools/event-emitte // have trailing newlines. And note that registerLogHandler actually registers // an error handler, despite its name. Subprocess.registerLogHandler( - function(s) console.error("subprocess: " + s.trim()) + s => console.error("subprocess: " + s.trim()) ); Subprocess.registerDebugHandler( - function(s) console.debug("subprocess: " + s.trim()) + s => console.debug("subprocess: " + s.trim()) ); function SimulatorProcess(options) { @@ -38,7 +38,9 @@ function SimulatorProcess(options) { SimulatorProcess.prototype = { // check if b2g is running - get isRunning() !!this.process, + get isRunning() { + return !!this.process; + }, /** * Start the process and connect the debugger client. diff --git a/chrome/test/unit/test_no_remote_registration.js b/chrome/test/unit/test_no_remote_registration.js index ac4f5745d7..0f55c7eca8 100644 --- a/chrome/test/unit/test_no_remote_registration.js +++ b/chrome/test/unit/test_no_remote_registration.js @@ -41,7 +41,7 @@ function ProtocolHandler(aScheme, aFlags) ProtocolHandler.prototype = { defaultPort: -1, - allowPort: function() false, + allowPort: () => false, newURI: function(aSpec, aCharset, aBaseURI) { let uri = Cc["@mozilla.org/network/standard-url;1"]. diff --git a/docshell/test/chrome/bug293235_window.xul b/docshell/test/chrome/bug293235_window.xul index 94aee8bbb2..b5b359d48c 100644 --- a/docshell/test/chrome/bug293235_window.xul +++ b/docshell/test/chrome/bug293235_window.xul @@ -64,7 +64,9 @@ }, }; os.addObserver(observer, URI_VISITED_RESOLUTION_TOPIC, false); - function notified() observer.notified; + function notified() { + return observer.notified; + } // Load a test page containing a link that should be initially // blue, per the :link style. diff --git a/dom/animation/AnimationPerformanceWarning.cpp b/dom/animation/AnimationPerformanceWarning.cpp index 21b91e3525..207bf05c54 100644 --- a/dom/animation/AnimationPerformanceWarning.cpp +++ b/dom/animation/AnimationPerformanceWarning.cpp @@ -52,15 +52,15 @@ AnimationPerformanceWarning::ToLocalizedString( case Type::TransformSVG: key = "AnimationWarningTransformSVG"; break; + case Type::TransformWithGeometricProperties: + key = "AnimationWarningTransformWithGeometricProperties"; + break; case Type::TransformFrameInactive: key = "AnimationWarningTransformFrameInactive"; break; case Type::OpacityFrameInactive: key = "AnimationWarningOpacityFrameInactive"; break; - case Type::WithGeometricProperties: - key = "AnimationWarningWithGeometricProperties"; - break; } nsresult rv = diff --git a/dom/animation/AnimationPerformanceWarning.h b/dom/animation/AnimationPerformanceWarning.h index 11481fe437..4eb25bad2e 100644 --- a/dom/animation/AnimationPerformanceWarning.h +++ b/dom/animation/AnimationPerformanceWarning.h @@ -21,9 +21,9 @@ struct AnimationPerformanceWarning TransformBackfaceVisibilityHidden, TransformPreserve3D, TransformSVG, + TransformWithGeometricProperties, TransformFrameInactive, OpacityFrameInactive, - WithGeometricProperties }; explicit AnimationPerformanceWarning(Type aType) diff --git a/dom/animation/AnimationUtils.cpp b/dom/animation/AnimationUtils.cpp index 1331203521..bab8bfa1f6 100644 --- a/dom/animation/AnimationUtils.cpp +++ b/dom/animation/AnimationUtils.cpp @@ -10,9 +10,12 @@ #include "nsDebug.h" #include "nsIAtom.h" #include "nsIContent.h" +#include "nsIDocument.h" +#include "nsGlobalWindow.h" #include "nsString.h" +#include "mozilla/Attributes.h" #include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction -#include "mozilla/dom/Element.h" // For dom::Element +#include "xpcpublic.h" // For xpc::NativeGlobal namespace mozilla { @@ -37,22 +40,18 @@ AnimationUtils::LogAsyncAnimationFailure(nsCString& aMessage, } /* static */ Maybe -AnimationUtils::ParseEasing(const dom::Element* aTarget, - const nsAString& aEasing) +AnimationUtils::ParseEasing(const nsAString& aEasing, + nsIDocument* aDocument) { - if (!aTarget) { - return Nothing(); - } - - nsIDocument* doc = aTarget->OwnerDoc(); + MOZ_ASSERT(aDocument); nsCSSValue value; nsCSSParser parser; parser.ParseLonghandProperty(eCSSProperty_animation_timing_function, aEasing, - doc->GetDocumentURI(), - doc->GetDocumentURI(), - doc->NodePrincipal(), + aDocument->GetDocumentURI(), + aDocument->GetDocumentURI(), + aDocument->NodePrincipal(), value); switch (value.GetUnit()) { @@ -69,7 +68,7 @@ AnimationUtils::ParseEasing(const dom::Element* aTarget, NS_STYLE_TRANSITION_TIMING_FUNCTION_LINEAR) { return Nothing(); } - // Fall through + MOZ_FALLTHROUGH; case eCSSUnit_Cubic_Bezier: case eCSSUnit_Steps: { nsTimingFunction timingFunction; @@ -98,4 +97,14 @@ AnimationUtils::ParseEasing(const dom::Element* aTarget, return Nothing(); } +/* static */ nsIDocument* +AnimationUtils::GetCurrentRealmDocument(JSContext* aCx) +{ + nsGlobalWindow* win = xpc::CurrentWindowOrNull(aCx); + if (!win) { + return nullptr; + } + return win->GetDoc(); +} + } // namespace mozilla diff --git a/dom/animation/AnimationUtils.h b/dom/animation/AnimationUtils.h index c048aaa029..afe8e20a2a 100644 --- a/dom/animation/AnimationUtils.h +++ b/dom/animation/AnimationUtils.h @@ -12,20 +12,18 @@ #include "nsStringFwd.h" class nsIContent; +class nsIDocument; +struct JSContext; namespace mozilla { class ComputedTimingFunction; -namespace dom { -class Element; -} - class AnimationUtils { public: static dom::Nullable - TimeDurationToDouble(const dom::Nullable& aTime) + TimeDurationToDouble(const dom::Nullable& aTime) { dom::Nullable result; @@ -37,7 +35,7 @@ public: } static dom::Nullable - DoubleToTimeDuration(const dom::Nullable& aTime) + DoubleToTimeDuration(const dom::Nullable& aTime) { dom::Nullable result; @@ -57,7 +55,13 @@ public: * be returned. */ static Maybe - ParseEasing(const dom::Element* aTarget, const nsAString& aEasing); + ParseEasing(const nsAString& aEasing, nsIDocument* aDocument); + + /** + * Get the document from the JS context to use when parsing CSS properties. + */ + static nsIDocument* + GetCurrentRealmDocument(JSContext* aCx); }; } // namespace mozilla diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp index 4c2c80e7ce..d686ca3cba 100644 --- a/dom/animation/EffectCompositor.cpp +++ b/dom/animation/EffectCompositor.cpp @@ -19,7 +19,6 @@ #include "nsComputedDOMStyle.h" // nsComputedDOMStyle::GetPresShellForContent #include "nsCSSPropertySet.h" #include "nsCSSProps.h" -#include "nsCSSPseudoElements.h" #include "nsIPresShell.h" #include "nsLayoutUtils.h" #include "nsRuleNode.h" // For nsRuleNode::ComputePropertiesOverridingAnimation @@ -83,11 +82,11 @@ FindAnimationsForCompositor(const nsIFrame* aFrame, // Those cases are probably not important but just to be safe, let's make // sure the cascade is up to date since if it *is* up to date, this is // basically a no-op. - Maybe> pseudoElement = + Maybe pseudoElement = EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame); if (pseudoElement) { - EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->first(), - pseudoElement->second(), + EffectCompositor::MaybeUpdateCascadeResults(pseudoElement->mElement, + pseudoElement->mPseudoType, aFrame->StyleContext()); } @@ -111,13 +110,15 @@ FindAnimationsForCompositor(const nsIFrame* aFrame, } AnimationPerformanceWarning::Type warningType; - if (effect->ShouldBlockCompositorAnimations(aFrame, - warningType)) { + if (aProperty == eCSSProperty_transform && + effect->ShouldBlockAsyncTransformAnimations(aFrame, + warningType)) { if (aMatches) { aMatches->Clear(); } - effect->SetPerformanceWarning( - aProperty, AnimationPerformanceWarning(warningType)); + EffectCompositor::SetPerformanceWarning( + aFrame, aProperty, + AnimationPerformanceWarning(warningType)); return false; } @@ -148,7 +149,7 @@ EffectCompositor::RequestRestyle(dom::Element* aElement, } auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel]; - PseudoElementHashKey key = { aElement, aPseudoType }; + PseudoElementHashEntry::KeyType key = { aElement, aPseudoType }; if (aRestyleType == RestyleType::Throttled) { if (!elementsToRestyle.Contains(key)) { @@ -230,7 +231,7 @@ EffectCompositor::MaybeUpdateAnimationRule(dom::Element* aElement, MaybeUpdateCascadeResults(aElement, aPseudoType); auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel]; - PseudoElementHashKey key = { aElement, aPseudoType }; + PseudoElementHashEntry::KeyType key = { aElement, aPseudoType }; if (!mPresContext || !elementsToRestyle.Contains(key)) { return; @@ -276,7 +277,7 @@ EffectCompositor::GetAnimationRule(dom::Element* aElement, #ifdef DEBUG { auto& elementsToRestyle = mElementsToRestyle[aCascadeLevel]; - PseudoElementHashKey key = { aElement, aPseudoType }; + PseudoElementHashEntry::KeyType key = { aElement, aPseudoType }; MOZ_ASSERT(!elementsToRestyle.Contains(key), "Element should no longer require a restyle after its " "animation rule has been updated"); @@ -362,7 +363,8 @@ EffectCompositor::AddStyleUpdatesTo(RestyleTracker& aTracker) // it will only mutate the bool value associated with each element in the // set but even doing that will cause assertions in PLDHashTable to fail // if we are iterating over the hashtable at the same time. - nsTArray elementsToRestyle(elementSet.Count()); + nsTArray elementsToRestyle( + elementSet.Count()); for (auto iter = elementSet.Iter(); !iter.Done(); iter.Next()) { elementsToRestyle.AppendElement(iter.Key()); } @@ -495,11 +497,11 @@ EffectCompositor::UpdateCascadeResults(Element* aElement, UpdateCascadeResults(*effects, aElement, aPseudoType, aStyleContext); } -/* static */ Maybe> +/* static */ Maybe EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame) { // Always return the same object to benefit from return-value optimization. - Maybe> result; + Maybe result; nsIContent* content = aFrame->GetContent(); if (!content) { @@ -536,7 +538,7 @@ EffectCompositor::GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame) return result; } - result = Some(MakePair(content->AsElement(), pseudoType)); + result.emplace(content->AsElement(), pseudoType); return result; } diff --git a/dom/animation/EffectCompositor.h b/dom/animation/EffectCompositor.h index fbcbd1bc25..b1cdbfbfc1 100644 --- a/dom/animation/EffectCompositor.h +++ b/dom/animation/EffectCompositor.h @@ -10,7 +10,7 @@ #include "mozilla/EnumeratedArray.h" #include "mozilla/Maybe.h" #include "mozilla/OwningNonNull.h" -#include "mozilla/Pair.h" +#include "mozilla/NonOwningAnimationTarget.h" #include "mozilla/PseudoElementHashEntry.h" #include "mozilla/RefPtr.h" #include "nsCSSProperty.h" @@ -29,7 +29,6 @@ namespace mozilla { class EffectSet; class RestyleTracker; -enum class CSSPseudoElementType : uint8_t; struct AnimationPerformanceWarning; namespace dom { @@ -185,7 +184,7 @@ public: // Returns an empty result when a suitable element cannot be found including // when the frame represents a pseudo-element on which we do not support // animations. - static Maybe> + static Maybe GetAnimationElementAndPseudoForFrame(const nsIFrame* aFrame); // Associates a performance warning with effects on |aFrame| that animates diff --git a/dom/animation/KeyframeEffect.cpp b/dom/animation/KeyframeEffect.cpp index 5c0c9a2f51..663e0c143e 100644 --- a/dom/animation/KeyframeEffect.cpp +++ b/dom/animation/KeyframeEffect.cpp @@ -6,6 +6,7 @@ #include "mozilla/dom/KeyframeEffect.h" +#include "mozilla/dom/AnimatableBinding.h" #include "mozilla/dom/KeyframeEffectBinding.h" #include "mozilla/dom/PropertyIndexedKeyframesBinding.h" #include "mozilla/AnimationUtils.h" @@ -572,15 +573,15 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, // FIXME: Maybe cache the current segment? const AnimationPropertySegment *segment = prop.mSegments.Elements(), *segmentEnd = segment + prop.mSegments.Length(); - while (segment->mToKey < computedTiming.mProgress.Value()) { - MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); + while (segment->mToKey <= computedTiming.mProgress.Value()) { + MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys"); if ((segment+1) == segmentEnd) { break; } ++segment; MOZ_ASSERT(segment->mFromKey == (segment-1)->mToKey, "incorrect keys"); } - MOZ_ASSERT(segment->mFromKey < segment->mToKey, "incorrect keys"); + MOZ_ASSERT(segment->mFromKey <= segment->mToKey, "incorrect keys"); MOZ_ASSERT(segment >= prop.mSegments.Elements() && size_t(segment - prop.mSegments.Elements()) < prop.mSegments.Length(), @@ -591,6 +592,18 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, aStyleRule = new AnimValuesStyleRule(); } + StyleAnimationValue* val = aStyleRule->AddEmptyValue(prop.mProperty); + + // Special handling for zero-length segments + if (segment->mToKey == segment->mFromKey) { + if (computedTiming.mProgress.Value() < 0) { + *val = segment->mFromValue; + } else { + *val = segment->mToValue; + } + continue; + } + double positionInSegment = (computedTiming.mProgress.Value() - segment->mFromKey) / (segment->mToKey - segment->mFromKey); @@ -598,8 +611,6 @@ KeyframeEffectReadOnly::ComposeStyle(RefPtr& aStyleRule, ComputedTimingFunction::GetPortion(segment->mTimingFunction, positionInSegment); - StyleAnimationValue *val = aStyleRule->AddEmptyValue(prop.mProperty); - #ifdef DEBUG bool result = #endif @@ -652,14 +663,27 @@ KeyframeEffectReadOnly::~KeyframeEffectReadOnly() { } -template +template /* static */ already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv) +KeyframeEffectReadOnly::ConstructKeyframeEffect( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const OptionsType& aOptions, + ErrorResult& aRv) { + nsIDocument* doc = AnimationUtils::GetCurrentRealmDocument(aGlobal.Context()); + if (!doc) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + TimingParams timingParams = + TimingParams::FromOptionsUnion(aOptions, doc, aRv); + if (aRv.Failed()) { + return nullptr; + } + if (aTarget.IsNull()) { // We don't support null targets yet. aRv.Throw(NS_ERROR_DOM_ANIM_NO_TARGET_ERR); @@ -694,29 +718,11 @@ KeyframeEffectReadOnly::ConstructKeyframeEffect(const GlobalObject& aGlobal, RefPtr effect = new KeyframeEffectType(targetElement->OwnerDoc(), targetElement, - pseudoType, aTiming); + pseudoType, timingParams); effect->mProperties = Move(animationProperties); return effect.forget(); } -// Explicit instantiations to avoid linker errors. - -template -already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv); - -template -already_AddRefed -KeyframeEffectReadOnly::ConstructKeyframeEffect<>(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv); - void KeyframeEffectReadOnly::ResetIsRunningOnCompositor() { @@ -1287,10 +1293,9 @@ GenerateValueEntries(Element* aTarget, for (OffsetIndexedKeyframe& keyframe : aKeyframes) { float offset = float(keyframe.mKeyframeDict.mOffset.Value()); - // ParseEasing uses element's owner doc, so if it is a pseudo element, - // we use its parent element's owner doc. Maybe easing = - AnimationUtils::ParseEasing(aTarget, keyframe.mKeyframeDict.mEasing); + AnimationUtils::ParseEasing(keyframe.mKeyframeDict.mEasing, + aTarget->OwnerDoc()); // We ignore keyframe.mKeyframeDict.mComposite since we don't support // composite modes on keyframes yet. @@ -1554,10 +1559,8 @@ BuildAnimationPropertyListFromPropertyIndexedKeyframes( return; } - // ParseEasing uses element's owner doc, so if it is a pseudo element, - // we use its parent element's owner doc. Maybe easing = - AnimationUtils::ParseEasing(aTarget, keyframes.mEasing); + AnimationUtils::ParseEasing(keyframes.mEasing, aTarget->OwnerDoc()); // We ignore easing.mComposite since we don't support composite modes on // keyframes yet. @@ -1757,6 +1760,19 @@ KeyframeEffectReadOnly::BuildAnimationPropertyList( } } +/* static */ already_AddRefed +KeyframeEffectReadOnly::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, + aFrames, aOptions, + aRv); +} + void KeyframeEffectReadOnly::GetTarget( Nullable& aRv) const @@ -1783,6 +1799,93 @@ KeyframeEffectReadOnly::GetTarget( } } +static void +CreatePropertyValue(nsCSSProperty aProperty, + float aOffset, + const Maybe& aTimingFunction, + const StyleAnimationValue& aValue, + AnimationPropertyValueDetails& aResult) +{ + aResult.mOffset = aOffset; + + nsString stringValue; + StyleAnimationValue::UncomputeValue(aProperty, aValue, stringValue); + aResult.mValue = stringValue; + + if (aTimingFunction) { + aResult.mEasing.Construct(); + aTimingFunction->AppendToString(aResult.mEasing.Value()); + } else { + aResult.mEasing.Construct(NS_LITERAL_STRING("linear")); + } + + aResult.mComposite = CompositeOperation::Replace; +} + +void +KeyframeEffectReadOnly::GetProperties( + nsTArray& aProperties, + ErrorResult& aRv) const +{ + for (const AnimationProperty& property : mProperties) { + AnimationPropertyDetails propertyDetails; + propertyDetails.mProperty = + NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(property.mProperty)); + propertyDetails.mRunningOnCompositor = property.mIsRunningOnCompositor; + + nsXPIDLString localizedString; + if (property.mPerformanceWarning && + property.mPerformanceWarning->ToLocalizedString(localizedString)) { + propertyDetails.mWarning.Construct(localizedString); + } + + if (!propertyDetails.mValues.SetCapacity(property.mSegments.Length(), + mozilla::fallible)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + return; + } + + for (size_t segmentIdx = 0, segmentLen = property.mSegments.Length(); + segmentIdx < segmentLen; + segmentIdx++) + { + const AnimationPropertySegment& segment = property.mSegments[segmentIdx]; + + binding_detail::FastAnimationPropertyValueDetails fromValue; + CreatePropertyValue(property.mProperty, segment.mFromKey, + segment.mTimingFunction, segment.mFromValue, + fromValue); + // We don't apply timing functions for zero-length segments, so + // don't return one here. + if (segment.mFromKey == segment.mToKey) { + fromValue.mEasing.Reset(); + } + // The following won't fail since we have already allocated the capacity + // above. + propertyDetails.mValues.AppendElement(fromValue, mozilla::fallible); + + // Normally we can ignore the to-value for this segment since it is + // identical to the from-value from the next segment. However, we need + // to add it if either: + // a) this is the last segment, or + // b) the next segment's from-value differs. + if (segmentIdx == segmentLen - 1 || + property.mSegments[segmentIdx + 1].mFromValue != segment.mToValue) { + binding_detail::FastAnimationPropertyValueDetails toValue; + CreatePropertyValue(property.mProperty, segment.mToKey, + Nothing(), segment.mToValue, toValue); + // It doesn't really make sense to have a timing function on the + // last property value or before a sudden jump so we just drop the + // easing property altogether. + toValue.mEasing.Reset(); + propertyDetails.mValues.AppendElement(toValue, mozilla::fallible); + } + } + + aProperties.AppendElement(propertyDetails); + } +} + void KeyframeEffectReadOnly::GetFrames(JSContext*& aCx, nsTArray& aResult, @@ -1881,32 +1984,6 @@ KeyframeEffectReadOnly::GetFrames(JSContext*& aCx, } } - -void -KeyframeEffectReadOnly::GetPropertyState( - nsTArray& aStates) const -{ - for (const AnimationProperty& property : mProperties) { - // Bug 1252730: We should also expose this winsInCascade as well. - if (!property.mWinsInCascade) { - continue; - } - - AnimationPropertyState state; - state.mProperty.Construct( - NS_ConvertASCIItoUTF16(nsCSSProps::GetStringValue(property.mProperty))); - state.mRunningOnCompositor.Construct(property.mIsRunningOnCompositor); - - nsXPIDLString localizedString; - if (property.mPerformanceWarning && - property.mPerformanceWarning->ToLocalizedString(localizedString)) { - state.mWarning.Construct(localizedString); - } - - aStates.AppendElement(state); - } -} - /* static */ const TimeDuration KeyframeEffectReadOnly::OverflowRegionRefreshInterval() { @@ -2135,7 +2212,7 @@ KeyframeEffectReadOnly::CanAnimateTransformOnCompositor( } bool -KeyframeEffectReadOnly::ShouldBlockCompositorAnimations( +KeyframeEffectReadOnly::ShouldBlockAsyncTransformAnimations( const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aPerformanceWarning) const { @@ -2155,7 +2232,7 @@ KeyframeEffectReadOnly::ShouldBlockCompositorAnimations( // Check for geometric properties if (IsGeometricProperty(property.mProperty)) { aPerformanceWarning = - AnimationPerformanceWarning::Type::WithGeometricProperties; + AnimationPerformanceWarning::Type::TransformWithGeometricProperties; return true; } @@ -2215,6 +2292,30 @@ KeyframeEffect::WrapObject(JSContext* aCx, return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto); } +/* static */ already_AddRefed +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, + aOptions, aRv); +} + +/* static */ already_AddRefed +KeyframeEffect::Constructor( + const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, + ErrorResult& aRv) +{ + return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, + aOptions, aRv); +} + void KeyframeEffect::NotifySpecifiedTimingUpdated() { nsIDocument* doc = nullptr; diff --git a/dom/animation/KeyframeEffect.h b/dom/animation/KeyframeEffect.h index 7f44bf1c18..8071a9fc8e 100644 --- a/dom/animation/KeyframeEffect.h +++ b/dom/animation/KeyframeEffect.h @@ -42,10 +42,11 @@ enum class CSSPseudoElementType : uint8_t; namespace dom { class ElementOrCSSPseudoElement; class OwningElementOrCSSPseudoElement; +class UnrestrictedDoubleOrKeyframeAnimationOptions; class UnrestrictedDoubleOrKeyframeEffectOptions; enum class IterationCompositeOperation : uint32_t; enum class CompositeOperation : uint32_t; -struct AnimationPropertyState; +struct AnimationPropertyDetails; } /** @@ -202,21 +203,14 @@ public: const Nullable& aTarget, JS::Handle aFrames, const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - ErrorResult& aRv) - { - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); - if (aRv.Failed()) { - return nullptr; - } - return ConstructKeyframeEffect( - aGlobal, aTarget, aFrames, timingParams, aRv); - } + ErrorResult& aRv); void GetTarget(Nullable& aRv) const; void GetFrames(JSContext*& aCx, nsTArray& aResult, ErrorResult& aRv); + void GetProperties(nsTArray& aProperties, + ErrorResult& aRv) const; // Temporary workaround to return both the target element and pseudo-type // until we implement PseudoElement (bug 1174575). @@ -312,26 +306,16 @@ public: bool IsRunningOnCompositor() const; void SetIsRunningOnCompositor(nsCSSProperty aProperty, bool aIsRunning); - void GetPropertyState(nsTArray& aStates) const; - - // Returns true if this effect, applied to |aFrame|, contains - // properties that mean we shouldn't run *any* compositor animations on this - // element. + // Returns true if this effect, applied to |aFrame|, contains properties + // that mean we shouldn't run transform compositor animations on this element. // // For example, if we have an animation of geometric properties like 'left' - // and 'top' on an element, we force all 'transform' and 'opacity' animations - // running at the same time on the same element to run on the main thread. + // and 'top' on an element, we force all 'transform' animations running at + // the same time on the same element to run on the main thread. // - // Similarly, some transform animations cannot be run on the compositor and - // when that is the case we simply disable all compositor animations - // on the same element. - // - // Bug 1218620 - It seems like we don't need to be this restrictive. Wouldn't - // it be ok to do 'opacity' animations on the compositor in either case? - // - // When returning true, |aOutPerformanceWarning| stores the reason why - // we shouldn't run the compositor animations. - bool ShouldBlockCompositorAnimations( + // When returning true, |aPerformanceWarning| stores the reason why + // we shouldn't run the transform animations. + bool ShouldBlockAsyncTransformAnimations( const nsIFrame* aFrame, AnimationPerformanceWarning::Type& aPerformanceWarning) const; @@ -354,12 +338,12 @@ protected: virtual ~KeyframeEffectReadOnly(); - template + template static already_AddRefed ConstructKeyframeEffect(const GlobalObject& aGlobal, const Nullable& aTarget, JS::Handle aFrames, - const TimingParams& aTiming, + const OptionsType& aOptions, ErrorResult& aRv); void ResetIsRunningOnCompositor(); @@ -433,29 +417,17 @@ public: const Nullable& aTarget, JS::Handle aFrames, const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - ErrorResult& aRv) - { - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aRv); - if (aRv.Failed()) { - return nullptr; - } - return ConstructKeyframeEffect( - aGlobal, aTarget, aFrames, timingParams, aRv); - } + ErrorResult& aRv); - // More generalized version for Animatable.animate. + // Variant of Constructor that accepts a KeyframeAnimationOptions object + // for use with for Animatable.animate. // Not exposed to content. static already_AddRefed - inline Constructor(const GlobalObject& aGlobal, - const Nullable& aTarget, - JS::Handle aFrames, - const TimingParams& aTiming, - ErrorResult& aRv) - { - return ConstructKeyframeEffect(aGlobal, aTarget, aFrames, - aTiming, aRv); - } + Constructor(const GlobalObject& aGlobal, + const Nullable& aTarget, + JS::Handle aFrames, + const UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, + ErrorResult& aRv); void NotifySpecifiedTimingUpdated(); diff --git a/dom/animation/NonOwningAnimationTarget.h b/dom/animation/NonOwningAnimationTarget.h new file mode 100644 index 0000000000..b35e45505e --- /dev/null +++ b/dom/animation/NonOwningAnimationTarget.h @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_NonOwningAnimationTarget_h +#define mozilla_NonOwningAnimationTarget_h + +#include "mozilla/Attributes.h" // For MOZ_NON_OWNING_REF +#include "nsCSSPseudoElements.h" + +namespace mozilla { + +namespace dom { +class Element; +} // namespace dom + +struct NonOwningAnimationTarget +{ + NonOwningAnimationTarget(dom::Element* aElement, CSSPseudoElementType aType) + : mElement(aElement), mPseudoType(aType) { } + + // mElement represents the parent element of a pseudo-element, not the + // generated content element. + dom::Element* MOZ_NON_OWNING_REF mElement = nullptr; + CSSPseudoElementType mPseudoType = CSSPseudoElementType::NotPseudo; +}; + +} // namespace mozilla + +#endif // mozilla_NonOwningAnimationTarget_h diff --git a/dom/animation/PseudoElementHashEntry.h b/dom/animation/PseudoElementHashEntry.h index b84f7c018d..12a20af17d 100644 --- a/dom/animation/PseudoElementHashEntry.h +++ b/dom/animation/PseudoElementHashEntry.h @@ -9,24 +9,17 @@ #include "mozilla/dom/Element.h" #include "mozilla/HashFunctions.h" +#include "mozilla/NonOwningAnimationTarget.h" #include "PLDHashTable.h" namespace mozilla { -enum class CSSPseudoElementType : uint8_t; - -struct PseudoElementHashKey -{ - dom::Element* mElement; - CSSPseudoElementType mPseudoType; -}; - // A hash entry that uses a RefPtr, CSSPseudoElementType pair class PseudoElementHashEntry : public PLDHashEntryHdr { public: - typedef PseudoElementHashKey KeyType; - typedef const PseudoElementHashKey* KeyTypePointer; + typedef NonOwningAnimationTarget KeyType; + typedef const NonOwningAnimationTarget* KeyTypePointer; explicit PseudoElementHashEntry(KeyTypePointer aKey) : mElement(aKey->mElement) @@ -35,7 +28,7 @@ public: ~PseudoElementHashEntry() = default; - KeyType GetKey() const { return {mElement, mPseudoType}; } + KeyType GetKey() const { return { mElement, mPseudoType }; } bool KeyEquals(KeyTypePointer aKey) const { return mElement == aKey->mElement && diff --git a/dom/animation/TimingParams.cpp b/dom/animation/TimingParams.cpp index 9c797d04ef..0092bfeefc 100644 --- a/dom/animation/TimingParams.cpp +++ b/dom/animation/TimingParams.cpp @@ -6,6 +6,8 @@ #include "mozilla/TimingParams.h" +#include "nsIDocument.h" + namespace mozilla { template @@ -32,10 +34,9 @@ GetTimingProperties( template static TimingParams -TimingParamsFromOptionsUnion( - const OptionsType& aOptions, - const Nullable& aTarget, - ErrorResult& aRv) +TimingParamsFromOptionsUnion(const OptionsType& aOptions, + nsIDocument* aDocument, + ErrorResult& aRv) { TimingParams result; if (aOptions.IsUnrestrictedDouble()) { @@ -47,21 +48,6 @@ TimingParamsFromOptionsUnion( aRv.Throw(NS_ERROR_DOM_TYPE_ERR); } } 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 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(); - } - } const dom::AnimationEffectTimingProperties& timing = GetTimingProperties(aOptions); @@ -82,8 +68,7 @@ TimingParamsFromOptionsUnion( result.mIterationStart = timing.mIterationStart; result.mDirection = timing.mDirection; result.mFill = timing.mFill; - result.mFunction = - AnimationUtils::ParseEasing(targetElement, timing.mEasing); + result.mFunction = AnimationUtils::ParseEasing(timing.mEasing, aDocument); } return result; } @@ -91,19 +76,19 @@ TimingParamsFromOptionsUnion( /* static */ TimingParams TimingParams::FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - const Nullable& aTarget, + nsIDocument* aDocument, ErrorResult& aRv) { - return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv); + return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv); } /* static */ TimingParams TimingParams::FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, - const Nullable& aTarget, + nsIDocument* aDocument, ErrorResult& aRv) { - return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv); + return TimingParamsFromOptionsUnion(aOptions, aDocument, aRv); } bool diff --git a/dom/animation/TimingParams.h b/dom/animation/TimingParams.h index 22cda11fe0..7c3a4320e6 100644 --- a/dom/animation/TimingParams.h +++ b/dom/animation/TimingParams.h @@ -17,17 +17,16 @@ #ifdef None #undef None #endif -#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode - // and PlaybackDirection +#include "mozilla/dom/AnimationEffectReadOnlyBinding.h" // for FillMode + // and PlaybackDirection + +class nsIDocument; namespace mozilla { namespace dom { -struct AnimationEffectTimingProperties; -class Element; class UnrestrictedDoubleOrKeyframeEffectOptions; class UnrestrictedDoubleOrKeyframeAnimationOptions; -class ElementOrCSSPseudoElement; } struct TimingParams @@ -36,12 +35,10 @@ struct TimingParams static TimingParams FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions, - const Nullable& aTarget, - ErrorResult& aRv); + nsIDocument* aDocument, ErrorResult& aRv); static TimingParams FromOptionsUnion( const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions, - const Nullable& aTarget, - ErrorResult& aRv); + nsIDocument* aDocument, ErrorResult& aRv); // Range-checks and validates an UnrestrictedDoubleOrString or // OwningUnrestrictedDoubleOrString object and converts to a diff --git a/dom/animation/moz.build b/dom/animation/moz.build index 1b1e7ee28a..8dcb1c1ad0 100644 --- a/dom/animation/moz.build +++ b/dom/animation/moz.build @@ -26,6 +26,7 @@ EXPORTS.mozilla += [ 'ComputedTimingFunction.h', 'EffectCompositor.h', 'EffectSet.h', + 'NonOwningAnimationTarget.h', 'PendingAnimationTracker.h', 'PseudoElementHashEntry.h', 'TimingParams.h', diff --git a/dom/animation/test/chrome.ini b/dom/animation/test/chrome.ini index ab725257ec..c63f67c09d 100644 --- a/dom/animation/test/chrome.ini +++ b/dom/animation/test/chrome.ini @@ -7,6 +7,7 @@ support-files = # file_animate_xrays.html needs to go in mochitest.ini since it is served # over HTTP [chrome/test_animation_observers.html] +[chrome/test_animation_properties.html] [chrome/test_animation_property_state.html] [chrome/test_restyles.html] [chrome/test_running_on_compositor.html] diff --git a/dom/animation/test/chrome/test_animation_properties.html b/dom/animation/test/chrome/test_animation_properties.html new file mode 100644 index 0000000000..00e94f8340 --- /dev/null +++ b/dom/animation/test/chrome/test_animation_properties.html @@ -0,0 +1,503 @@ + + + +Bug 1254419 - Test the values returned by + KeyframeEffectReadOnly.getProperties() + + + + + +Mozilla Bug 1254419 +
+ + + diff --git a/dom/animation/test/chrome/test_animation_property_state.html b/dom/animation/test/chrome/test_animation_property_state.html index 0dcce0200d..a2129e29c9 100644 --- a/dom/animation/test/chrome/test_animation_property_state.html +++ b/dom/animation/test/chrome/test_animation_property_state.html @@ -1,8 +1,8 @@ -Bug 1196114 - Animation property which indicates - running on the compositor or not +Bug 1196114 - Test metadata related to which animation properties + are running on the compositor @@ -13,6 +13,10 @@ height: 100px; background-color: white; } +@keyframes fade { + from { opacity: 1 } + to { opacity: 0 } +} @@ -41,7 +45,7 @@ function compare_property_state(a, b) { } function assert_animation_property_state_equals(actual, expected) { - assert_equals(actual.length, expected.length); + assert_equals(actual.length, expected.length, 'Number of properties'); var sortedActual = actual.sort(compare_property_state); var sortedExpected = expected.sort(compare_property_state); @@ -79,7 +83,8 @@ function assert_property_state_on_compositor(actual, expected) { sortedExpected[i].property, 'CSS property name should match'); assert_true(sortedActual[i].runningOnCompositor, - 'runningOnCompositor property should be true'); + 'runningOnCompositor property should be true on ' + + sortedActual[i].property); assert_not_exists(sortedActual[i], 'warning', 'warning property should not be set'); } @@ -144,11 +149,28 @@ var gAnimationsTests = [ } ] }, + { + desc: 'opacity on compositor with animation of geometric properties', + frames: { + width: ['100px', '200px'], + opacity: [0, 1] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false + }, + { + property: 'opacity', + runningOnCompositor: true + } + ] + }, { // FIXME: Once we have KeyframeEffect.setFrames, we should rewrite // this test case to check that runningOnCompositor is restored to true // after 'width' keyframe is removed from the keyframes. - desc: 'animation on compositor with animation of geometric properties', + desc: 'transform on compositor with animation of geometric properties', frames: { width: ['100px', '200px'], transform: ['translate(0px)', 'translate(100px)'] @@ -161,7 +183,31 @@ var gAnimationsTests = [ { property: 'transform', runningOnCompositor: false, - warning: 'AnimationWarningWithGeometricProperties' + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + desc: 'opacity and transform on compositor with animation of geometric ' + + 'properties', + frames: { + width: ['100px', '200px'], + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false + }, + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' } ] }, @@ -173,7 +219,7 @@ gAnimationsTests.forEach(function(subtest) { var animation = div.animate(subtest.frames, 100000); return animation.ready.then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), subtest.expected); })); }, subtest.desc); @@ -208,6 +254,243 @@ var gPerformanceWarningTests = [ } ] }, + { + desc: 'opacity and transform with preserve-3d', + frames: { + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + style: 'transform-style: preserve-3d', + expected: [ + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformPreserve3D' + } + ] + }, + { + desc: 'opacity and transform with backface-visibility:hidden', + frames: { + opacity: [0, 1], + transform: ['translate(0px)', 'translate(100px)'] + }, + style: 'backface-visibility: hidden;', + expected: [ + { + property: 'opacity', + runningOnCompositor: true + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformBackfaceVisibilityHidden' + } + ] + }, +]; + +var gMultipleAsyncAnimationsTests = [ + { + desc: 'opacity and transform with preserve-3d', + style: 'transform-style: preserve-3d', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformPreserve3D' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, + { + desc: 'opacity and transform with backface-visibility:hidden', + style: 'backface-visibility: hidden;', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformBackfaceVisibilityHidden' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, +]; + +// FIXME: Once we have KeyframeEffect.setFrames, we should rewrite +// these test cases to check that runningOnCompositor is restored to true +// after 'width' keyframe is removed from the keyframes. +var gMultipleAsyncAnimationsWithGeometricKeyframeTests = [ + { + desc: 'transform and opacity with animation of geometric properties', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + width: ['100px', '200px'], + opacity: [0, 1] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false, + }, + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, + { + desc: 'opacity and transform with animation of geometric properties', + animations: [ + { + frames: { + width: ['100px', '200px'], + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'width', + runningOnCompositor: false, + }, + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, +]; + +// Test cases that check results of adding/removing 'width' animation on the +// same element which has async animations. +var gMultipleAsyncAnimationsWithGeometricAnimationTests = [ + { + desc: 'transform', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + ] + }, + { + desc: 'opacity', + animations: [ + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true + } + ] + }, + ] + }, + { + desc: 'opacity and transform', + animations: [ + { + frames: { + transform: ['translate(0px)', 'translate(100px)'] + }, + expected: [ + { + property: 'transform', + runningOnCompositor: false, + warning: 'AnimationWarningTransformWithGeometricProperties' + } + ] + }, + { + frames: { + opacity: [0, 1] + }, + expected: [ + { + property: 'opacity', + runningOnCompositor: true, + } + ] + } + ], + }, ]; function start() { @@ -222,7 +505,7 @@ function start() { var animation = div.animate(subtest.frames, 100000); return animation.ready.then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), subtest.expected); })); }, subtest.desc); @@ -234,31 +517,132 @@ function start() { var animation = div.animate(subtest.frames, 100000); return animation.ready.then(t.step_func(function() { assert_property_state_on_compositor( - animation.effect.getPropertyState(), + animation.effect.getProperties(), subtest.expected); div.style = subtest.style; return waitForFrame(); })).then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), subtest.expected); div.style = ''; return waitForFrame(); })).then(t.step_func(function() { assert_property_state_on_compositor( - animation.effect.getPropertyState(), + animation.effect.getProperties(), subtest.expected); })); }, subtest.desc); }); + gMultipleAsyncAnimationsTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getProperties(), + anim.expected); + })); + div.style = subtest.style; + return waitForFrame(); + })).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getProperties(), + anim.expected); + })); + div.style = ''; + return waitForFrame(); + })).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getProperties(), + anim.expected); + })); + })); + }, 'Multiple animations: ' + subtest.desc); + }); + + gMultipleAsyncAnimationsWithGeometricKeyframeTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getProperties(), + anim.expected); + })); + })); + }, 'Multiple animations with geometric property: ' + subtest.desc); + }); + + gMultipleAsyncAnimationsWithGeometricAnimationTests.forEach(function(subtest) { + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable' }); + var animations = subtest.animations.map(function(anim) { + var animation = div.animate(anim.frames, 100000); + + // Bind expected values to animation object. + animation.expected = anim.expected; + return animation; + }); + + var widthAnimation; + + return waitForAllAnimations(animations).then(t.step_func(function() { + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getProperties(), + anim.expected); + })); + })).then(t.step_func(function() { + // Append 'width' animation on the same element. + widthAnimation = div.animate({ width: ['100px', '200px'] }, 100000); + return waitForFrame(); + })).then(t.step_func(function() { + // Now transform animations are not running on compositor because of + // the 'width' animation. + animations.forEach(t.step_func(function(anim) { + assert_animation_property_state_equals( + anim.effect.getProperties(), + anim.expected); + })); + // Remove the 'width' animation. + widthAnimation.cancel(); + return waitForFrame(); + })).then(t.step_func(function() { + // Now all animations are running on compositor. + animations.forEach(t.step_func(function(anim) { + assert_property_state_on_compositor( + anim.effect.getProperties(), + anim.expected); + })); + })); + }, 'Multiple async animations and geometric animation: ' + subtest.desc); + }); + promise_test(function(t) { var div = addDiv(t, { class: 'compositable' }); var animation = div.animate( { transform: ['translate(0px)', 'translate(100px)'] }, 100000); return animation.ready.then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: true } ]); div.style = 'width: 10000px; height: 10000px'; return waitForFrame(); @@ -269,7 +653,7 @@ function start() { "bigger than the viewport \\(\\d+, \\d+\\) or the visual rectangle " + "\\(10000, 10000\\) is larger than the max allowed value \\(\\d+\\)"); assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: false, @@ -280,7 +664,7 @@ function start() { })).then(t.step_func(function() { // FIXME: Bug 1253164: the animation should get back on compositor. assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: false } ]); })); }, 'transform on too big element'); @@ -303,13 +687,13 @@ function start() { { transform: ['translate(0px)', 'translate(100px)'] }, 100000); return animation.ready.then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: true } ]); svg.setAttribute('transform', 'translate(10, 20)'); return waitForFrame(); })).then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: false, @@ -319,10 +703,25 @@ function start() { return waitForFrame(); })).then(t.step_func(function() { assert_animation_property_state_equals( - animation.effect.getPropertyState(), + animation.effect.getProperties(), [ { property: 'transform', runningOnCompositor: true } ]); })); }, 'transform of nsIFrame with SVG transform'); + + promise_test(function(t) { + var div = addDiv(t, { class: 'compositable', + style: 'animation: fade 100s' }); + var cssAnimation = div.getAnimations()[0]; + var scriptAnimation = div.animate({ opacity: [ 1, 0 ] }, 1000); + return scriptAnimation.ready.then(function() { + assert_animation_property_state_equals( + cssAnimation.effect.getProperties(), + [ { property: 'opacity', runningOnCompositor: false } ]); + assert_animation_property_state_equals( + scriptAnimation.effect.getProperties(), + [ { property: 'opacity', runningOnCompositor: true } ]); + }); + }, 'overridden animation'); } diff --git a/dom/base/DOMRequest.cpp b/dom/base/DOMRequest.cpp index 1f31874da4..2d791525b5 100644 --- a/dom/base/DOMRequest.cpp +++ b/dom/base/DOMRequest.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/Promise.h" #include "mozilla/dom/ScriptSettings.h" #include "jsfriendapi.h" +#include "nsContentUtils.h" using mozilla::dom::AnyCallback; using mozilla::dom::DOMError; @@ -298,11 +299,10 @@ DOMRequestService::FireDetailedError(nsIDOMDOMRequest* aRequest, class FireSuccessAsyncTask : public nsRunnable { - FireSuccessAsyncTask(JSContext* aCx, - DOMRequest* aRequest, + FireSuccessAsyncTask(DOMRequest* aRequest, const JS::Value& aResult) : mReq(aRequest), - mResult(aCx, aResult) + mResult(nsContentUtils::RootingCxForThread(), aResult) { } @@ -315,8 +315,8 @@ public: Dispatch(DOMRequest* aRequest, const JS::Value& aResult) { - mozilla::ThreadsafeAutoSafeJSContext cx; - RefPtr asyncTask = new FireSuccessAsyncTask(cx, aRequest, aResult); + RefPtr asyncTask = + new FireSuccessAsyncTask(aRequest, aResult); MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(asyncTask))); return NS_OK; } diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 5b981c2fb3..34ebeb3cc0 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3425,14 +3425,8 @@ Element::Animate(const Nullable& aTarget, } } - TimingParams timingParams = - TimingParams::FromOptionsUnion(aOptions, aTarget, aError); - if (aError.Failed()) { - return nullptr; - } - RefPtr effect = - KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError); + KeyframeEffect::Constructor(global, aTarget, frames, aOptions, aError); if (aError.Failed()) { return nullptr; } diff --git a/dom/base/Navigator.h b/dom/base/Navigator.h index 50105cf85c..74703d628a 100644 --- a/dom/base/Navigator.h +++ b/dom/base/Navigator.h @@ -149,6 +149,10 @@ public: // The XPCOM GetProduct is OK // The XPCOM GetLanguage is OK + void GetUserAgent(nsString& aUserAgent, ErrorResult& /* unused */) + { + GetUserAgent(aUserAgent); + } bool OnLine(); void RegisterProtocolHandler(const nsAString& aScheme, const nsAString& aURL, const nsAString& aTitle, ErrorResult& aRv); diff --git a/dom/base/ScriptSettings.cpp b/dom/base/ScriptSettings.cpp index f79e173e0b..057d643725 100644 --- a/dom/base/ScriptSettings.cpp +++ b/dom/base/ScriptSettings.cpp @@ -883,26 +883,4 @@ AutoSafeJSContext::AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMP "returned null, and inited correctly otherwise!"); } -ThreadsafeAutoSafeJSContext::ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL) -{ - MOZ_GUARD_OBJECT_NOTIFIER_INIT; - - if (NS_IsMainThread()) { - mCx = nullptr; - mAutoSafeJSContext.emplace(); - } else { - mCx = mozilla::dom::workers::GetCurrentThreadJSContext(); - mRequest.emplace(mCx); - } -} - -ThreadsafeAutoSafeJSContext::operator JSContext*() const -{ - if (mCx) { - return mCx; - } else { - return *mAutoSafeJSContext; - } -} - } // namespace mozilla diff --git a/dom/base/ScriptSettings.h b/dom/base/ScriptSettings.h index 7264347e7e..4236e7fa8f 100644 --- a/dom/base/ScriptSettings.h +++ b/dom/base/ScriptSettings.h @@ -477,22 +477,6 @@ private: MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER }; -/** - * Like AutoSafeJSContext but can be used safely on worker threads. - */ -class MOZ_RAII ThreadsafeAutoSafeJSContext { -public: - explicit ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM); - operator JSContext*() const; - -private: - JSContext* mCx; // Used on workers. Null means mainthread. - Maybe mRequest; // Used on workers. - Maybe mAutoSafeJSContext; // Used on main thread. - MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - - } // namespace mozilla #endif // mozilla_dom_ScriptSettings_h diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index c00bf1e3a7..00d74de551 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -511,7 +511,6 @@ DOMInterfaces = { }, 'FileReaderSync': { - 'workers': True, 'wrapperCache': False, }, @@ -1620,14 +1619,7 @@ DOMInterfaces = { }, }, -'WorkerLocation': { - 'headerFile': 'mozilla/dom/workers/bindings/Location.h', - 'workers': True, -}, - 'WorkerNavigator': { - 'headerFile': 'mozilla/dom/workers/bindings/Navigator.h', - 'workers': True, 'implicitJSContext': ['getDataStores'], }, diff --git a/dom/events/CustomEvent.cpp b/dom/events/CustomEvent.cpp index 0094b4124a..a6197e0400 100644 --- a/dom/events/CustomEvent.cpp +++ b/dom/events/CustomEvent.cpp @@ -17,21 +17,30 @@ using namespace mozilla::dom; CustomEvent::CustomEvent(mozilla::dom::EventTarget* aOwner, nsPresContext* aPresContext, mozilla::WidgetEvent* aEvent) -: Event(aOwner, aPresContext, aEvent) + : Event(aOwner, aPresContext, aEvent) + , mDetail(JS::NullValue()) { + mozilla::HoldJSObjects(this); } -CustomEvent::~CustomEvent() {} +CustomEvent::~CustomEvent() +{ + mozilla::DropJSObjects(this); +} NS_IMPL_CYCLE_COLLECTION_CLASS(CustomEvent) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CustomEvent, Event) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDetail) + tmp->mDetail.setUndefined(); + mozilla::DropJSObjects(this); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CustomEvent, Event) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDetail) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CustomEvent, Event) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDetail) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + NS_IMPL_ADDREF_INHERITED(CustomEvent, Event) NS_IMPL_RELEASE_INHERITED(CustomEvent, Event) @@ -66,8 +75,21 @@ CustomEvent::InitCustomEvent(const nsAString& aType, bool aCancelable, nsIVariant* aDetail) { + AutoJSAPI jsapi; + NS_ENSURE_STATE(jsapi.Init(GetParentObject())); + JSContext* cx = jsapi.cx(); + JS::Rooted detail(cx); + + if (!aDetail) { + detail = JS::NullValue(); + } else if (NS_WARN_IF(!VariantToJsval(cx, aDetail, &detail))) { + JS_ClearPendingException(cx); + return NS_ERROR_FAILURE; + } + Event::InitEvent(aType, aCanBubble, aCancelable); - mDetail = aDetail; + mDetail = detail; + return NS_OK; } @@ -79,35 +101,36 @@ CustomEvent::InitCustomEvent(JSContext* aCx, JS::Handle aDetail, ErrorResult& aRv) { - nsCOMPtr detail; - if (nsIXPConnect* xpc = nsContentUtils::XPConnect()) { - xpc->JSToVariant(aCx, aDetail, getter_AddRefs(detail)); - } - - if (!detail) { - aRv.Throw(NS_ERROR_FAILURE); - return; - } - InitCustomEvent(aType, aCanBubble, aCancelable, detail); + Event::InitEvent(aType, aCanBubble, aCancelable); + mDetail = aDetail; } NS_IMETHODIMP CustomEvent::GetDetail(nsIVariant** aDetail) { - NS_IF_ADDREF(*aDetail = mDetail); - return NS_OK; + if (mDetail.isNull()) { + *aDetail = nullptr; + return NS_OK; + } + + AutoJSAPI jsapi; + NS_ENSURE_STATE(jsapi.Init(GetParentObject())); + JSContext* cx = jsapi.cx(); + JS::Rooted detail(cx, mDetail); + nsIXPConnect* xpc = nsContentUtils::XPConnect(); + + if (NS_WARN_IF(!xpc)) { + return NS_ERROR_FAILURE; + } + + return xpc->JSToVariant(cx, detail, aDetail); } void CustomEvent::GetDetail(JSContext* aCx, JS::MutableHandle aRetval) { - if (!mDetail) { - aRetval.setNull(); - return; - } - - VariantToJsval(aCx, mDetail, aRetval); + aRetval.set(mDetail); } already_AddRefed diff --git a/dom/events/CustomEvent.h b/dom/events/CustomEvent.h index 24cd0efa0e..d14243cff0 100644 --- a/dom/events/CustomEvent.h +++ b/dom/events/CustomEvent.h @@ -21,7 +21,7 @@ class CustomEvent final : public Event, private: virtual ~CustomEvent(); - nsCOMPtr mDetail; + JS::Heap mDetail; public: explicit CustomEvent(mozilla::dom::EventTarget* aOwner, @@ -29,7 +29,7 @@ public: mozilla::WidgetEvent* aEvent = nullptr); NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CustomEvent, Event) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(CustomEvent, Event) NS_FORWARD_TO_EVENT NS_DECL_NSIDOMCUSTOMEVENT diff --git a/dom/events/test/mochitest.ini b/dom/events/test/mochitest.ini index 51e894c205..198a1c471a 100644 --- a/dom/events/test/mochitest.ini +++ b/dom/events/test/mochitest.ini @@ -47,6 +47,8 @@ skip-if = buildapp == 'b2g' || e10s # b2g(2 failures out of 8, mousewheel test) [test_bug426082.html] skip-if = buildapp == 'mulet' || buildapp == 'b2g' || os == "win" || toolkit == 'android' || e10s # Intermittent failures, bug 921693 # b2g(1 failure out of 6, Moving the mouse down from the label should have unpressed the button) b2g-debug(1 failure out of 6, Moving the mouse down from the label should have unpressed the button) b2g-desktop(1 failure out of 6, Moving the mouse down from the label should have unpressed the button) [test_bug427537.html] +[test_bug1003432.html] +support-files = test_bug1003432.js [test_bug428988.html] [test_bug432698.html] skip-if = buildapp == 'mulet' diff --git a/dom/events/test/test_bug1003432.html b/dom/events/test/test_bug1003432.html new file mode 100644 index 0000000000..7ffd3f7c4c --- /dev/null +++ b/dom/events/test/test_bug1003432.html @@ -0,0 +1,45 @@ + + + + + Test for Bug 1003432 + + + + +Mozilla Bug 1003432 +

+ +
+
+
+ + diff --git a/dom/events/test/test_bug1003432.js b/dom/events/test/test_bug1003432.js new file mode 100644 index 0000000000..1ace8fefd7 --- /dev/null +++ b/dom/events/test/test_bug1003432.js @@ -0,0 +1,23 @@ +addEventListener("foobar", + function(evt) { + postMessage( + { + type: evt.type, + bubbles: evt.bubbles, + cancelable: evt.cancelable, + detail: evt.detail + }); + }, true); + +addEventListener("message", + function(evt) { + // Test the constructor of CustomEvent + var e = new CustomEvent("foobar", + {bubbles:true, cancelable: true, detail:"test"}); + dispatchEvent(e); + + // Test initCustomEvent + e = new CustomEvent("foobar"); + e.initCustomEvent("foobar", true, true, "test"); + dispatchEvent(e); + }, true); diff --git a/dom/events/test/test_bug427537.html b/dom/events/test/test_bug427537.html index 6ca1a4c8a8..060581f2f6 100644 --- a/dom/events/test/test_bug427537.html +++ b/dom/events/test/test_bug427537.html @@ -39,6 +39,10 @@ document.addEventListener("foobar", document.dispatchEvent(e); ok(didCallListener, "Should have called listener!"); +e = document.createEvent("CustomEvent"); +e.initEvent("foo", true, true); +is(e.detail, null, "Default detail should be null."); + e = document.createEvent("CustomEvent"); e.initCustomEvent("foobar", true, true, 1); is(e.detail, 1, "Detail should be 1."); diff --git a/dom/locales/en-US/chrome/layout/layout_errors.properties b/dom/locales/en-US/chrome/layout/layout_errors.properties index 7ae736b25d..e43bcd7c84 100644 --- a/dom/locales/en-US/chrome/layout/layout_errors.properties +++ b/dom/locales/en-US/chrome/layout/layout_errors.properties @@ -24,11 +24,11 @@ AnimationWarningTransformBackfaceVisibilityHidden=Async animation of 'backface-v ## 'transform-style: preserve-3d' is a CSS property, don't translate it. AnimationWarningTransformPreserve3D=Async animation of 'transform-style: preserve-3d' transforms is not supported ## LOCALIZATION NOTE(AnimationWarningTransformSVG, +## AnimationWarningTransformWithGeometricProperties, ## AnimationWarningTransformFrameInactive, -## AnimationWarningOpacityFrameInactive, -## AnimationWarningWithGeometricProperties): +## AnimationWarningOpacityFrameInactive): ## 'transform' and 'opacity' mean CSS property names, don't translate it. AnimationWarningTransformSVG=Async 'transform' animations of aFrames with SVG transforms is not supported +AnimationWarningTransformWithGeometricProperties=Async animation of 'transform' not possible due to animation of geometric properties on the same element AnimationWarningTransformFrameInactive=Async animation disabled because frame was not marked active for 'transform' animation AnimationWarningOpacityFrameInactive=Async animation disabled because frame was not marked active for 'opacity' animation -AnimationWarningWithGeometricProperties=Async animation of 'transform' or 'opacity' not possible due to animation of geometric properties on the same element diff --git a/dom/promise/Promise.cpp b/dom/promise/Promise.cpp index cb93c03cad..079ade8015 100644 --- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -1762,21 +1762,21 @@ public: { MOZ_ASSERT(mCountdown > 0); - ThreadsafeAutoSafeJSContext cx; - JSAutoCompartment ac(cx, mValues); - { + AutoJSAPI jsapi; + if (!jsapi.Init(mValues)) { + return; + } + jsapi.TakeOwnershipOfErrorReporting(); + JSContext* cx = jsapi.cx(); - AutoDontReportUncaught silenceReporting(cx); - JS::Rooted value(cx, aValue); - JS::Rooted values(cx, mValues); - if (!JS_WrapValue(cx, &value) || - !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) { - MOZ_ASSERT(JS_IsExceptionPending(cx)); - JS::Rooted exn(cx); - JS_GetPendingException(cx, &exn); - - mPromise->MaybeReject(cx, exn); - } + JS::Rooted value(cx, aValue); + JS::Rooted values(cx, mValues); + if (!JS_WrapValue(cx, &value) || + !JS_DefineElement(cx, values, index, value, JSPROP_ENUMERATE)) { + MOZ_ASSERT(JS_IsExceptionPending(cx)); + JS::Rooted exn(cx); + jsapi.StealException(&exn); + mPromise->MaybeReject(cx, exn); } --mCountdown; diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 1854866672..efa51198aa 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -834,7 +834,7 @@ var interfaceNamesInGlobalScope = // IMPORTANT: Do not change this list without review from a DOM peer! "mozRTCSessionDescription", // IMPORTANT: Do not change this list without review from a DOM peer! - "MozSettingsEvent", + {name: "MozSettingsEvent", permission: ["settings-api-read"]}, // IMPORTANT: Do not change this list without review from a DOM peer! {name: "MozSettingsTransactionEvent", permission: ["settings-api-read"]}, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/webidl/CustomEvent.webidl b/dom/webidl/CustomEvent.webidl index 180159b33a..299a41ec09 100644 --- a/dom/webidl/CustomEvent.webidl +++ b/dom/webidl/CustomEvent.webidl @@ -10,7 +10,8 @@ * liability, trademark and document use rules apply. */ -[Constructor(DOMString type, optional CustomEventInit eventInitDict)] +[Constructor(DOMString type, optional CustomEventInit eventInitDict), + Exposed=(Window, Worker)] interface CustomEvent : Event { readonly attribute any detail; diff --git a/dom/webidl/KeyframeEffect.webidl b/dom/webidl/KeyframeEffect.webidl index 2d748b21fa..47f3b351f0 100644 --- a/dom/webidl/KeyframeEffect.webidl +++ b/dom/webidl/KeyframeEffect.webidl @@ -46,14 +46,22 @@ interface KeyframeEffectReadOnly : AnimationEffectReadOnly { }; // Non-standard extensions -dictionary AnimationPropertyState { - DOMString property; - boolean runningOnCompositor; - DOMString? warning; +dictionary AnimationPropertyValueDetails { + required double offset; + required DOMString value; + DOMString easing; + required CompositeOperation composite; +}; + +dictionary AnimationPropertyDetails { + required DOMString property; + required boolean runningOnCompositor; + DOMString warning; + required sequence values; }; partial interface KeyframeEffectReadOnly { - [ChromeOnly] sequence getPropertyState(); + [ChromeOnly, Throws] sequence getProperties(); }; [Func="nsDocument::IsWebAnimationsEnabled", diff --git a/dom/webidl/MozSettingsEvent.webidl b/dom/webidl/MozSettingsEvent.webidl index 7f047f16d0..ba043736ad 100644 --- a/dom/webidl/MozSettingsEvent.webidl +++ b/dom/webidl/MozSettingsEvent.webidl @@ -4,7 +4,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ -[Constructor(DOMString type, optional MozSettingsEventInit eventInitDict)] +[Constructor(DOMString type, optional MozSettingsEventInit eventInitDict), + CheckAnyPermissions="settings-api-read settings-api-write"] interface MozSettingsEvent : Event { readonly attribute DOMString? settingName; diff --git a/dom/webidl/Navigator.webidl b/dom/webidl/Navigator.webidl index 2a856a3bdb..dbbb7c31d1 100644 --- a/dom/webidl/Navigator.webidl +++ b/dom/webidl/Navigator.webidl @@ -43,7 +43,7 @@ interface NavigatorID { readonly attribute DOMString appVersion; [Constant, Cached] readonly attribute DOMString platform; - [Pure, Cached, Throws=Workers] + [Pure, Cached, Throws] readonly attribute DOMString userAgent; [Constant, Cached] readonly attribute DOMString product; // constant "Gecko" diff --git a/dom/webidl/WorkerDebuggerGlobalScope.webidl b/dom/webidl/WorkerDebuggerGlobalScope.webidl index 06d07690ab..70e477ca68 100644 --- a/dom/webidl/WorkerDebuggerGlobalScope.webidl +++ b/dom/webidl/WorkerDebuggerGlobalScope.webidl @@ -5,6 +5,7 @@ [Global=(WorkerDebugger), Exposed=WorkerDebugger] interface WorkerDebuggerGlobalScope : EventTarget { + [Throws] readonly attribute object global; object createSandbox(DOMString name, object prototype); diff --git a/dom/workers/FileReaderSync.cpp b/dom/workers/FileReaderSync.cpp index 4620544e48..57f5852f6a 100644 --- a/dom/workers/FileReaderSync.cpp +++ b/dom/workers/FileReaderSync.cpp @@ -27,7 +27,6 @@ #include "RuntimeService.h" -USING_WORKERS_NAMESPACE using namespace mozilla; using namespace mozilla::dom; using mozilla::dom::Optional; @@ -47,7 +46,7 @@ FileReaderSync::WrapObject(JSContext* aCx, JS::Handle aGivenProto, JS::MutableHandle aReflector) { - return FileReaderSyncBinding_workers::Wrap(aCx, this, aGivenProto, aReflector); + return FileReaderSyncBinding::Wrap(aCx, this, aGivenProto, aReflector); } void diff --git a/dom/workers/FileReaderSync.h b/dom/workers/FileReaderSync.h index 2d097233d8..db8f9d574e 100644 --- a/dom/workers/FileReaderSync.h +++ b/dom/workers/FileReaderSync.h @@ -4,8 +4,8 @@ * 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_workers_filereadersync_h__ -#define mozilla_dom_workers_filereadersync_h__ +#ifndef mozilla_dom_filereadersync_h__ +#define mozilla_dom_filereadersync_h__ #include "Workers.h" @@ -18,10 +18,6 @@ namespace dom { class Blob; class GlobalObject; template class Optional; -} // namespace dom -} // namespace mozilla - -BEGIN_WORKERS_NAMESPACE class FileReaderSync final { @@ -51,6 +47,7 @@ public: void ReadAsDataURL(Blob& aBlob, nsAString& aResult, ErrorResult& aRv); }; -END_WORKERS_NAMESPACE +} // namespace dom +} // namespace mozilla -#endif // mozilla_dom_workers_filereadersync_h__ +#endif // mozilla_dom_filereadersync_h__ diff --git a/dom/workers/ServiceWorkerEvents.cpp b/dom/workers/ServiceWorkerEvents.cpp index a4b2b73db2..b54193bdf2 100644 --- a/dom/workers/ServiceWorkerEvents.cpp +++ b/dom/workers/ServiceWorkerEvents.cpp @@ -53,6 +53,47 @@ using namespace mozilla::dom; using namespace mozilla::dom::workers; +namespace { + +void +AsyncLog(nsIInterceptedChannel *aInterceptedChannel, + const nsACString& aRespondWithScriptSpec, + uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, + const nsACString& aMessageName, const nsTArray& aParams) +{ + MOZ_ASSERT(aInterceptedChannel); + nsCOMPtr reporter = + aInterceptedChannel->GetConsoleReportCollector(); + if (reporter) { + reporter->AddConsoleReport(nsIScriptError::errorFlag, + NS_LITERAL_CSTRING("Service Worker Interception"), + nsContentUtils::eDOM_PROPERTIES, + aRespondWithScriptSpec, + aRespondWithLineNumber, + aRespondWithColumnNumber, + aMessageName, aParams); + } +} + +template +void +AsyncLog(nsIInterceptedChannel* aInterceptedChannel, + const nsACString& aRespondWithScriptSpec, + uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, + // We have to list one explicit string so that calls with an + // nsTArray of params won't end up in here. + const nsACString& aMessageName, const nsAString& aFirstParam, + Params&&... aParams) +{ + nsTArray paramsList(sizeof...(Params) + 1); + StringArrayAppender::Append(paramsList, sizeof...(Params) + 1, + aFirstParam, Forward(aParams)...); + AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber, + aRespondWithColumnNumber, aMessageName, paramsList); +} + +} // anonymous namespace + BEGIN_WORKERS_NAMESPACE CancelChannelRunnable::CancelChannelRunnable(nsMainThreadPtrHandle& aChannel, @@ -112,43 +153,6 @@ FetchEvent::Constructor(const GlobalObject& aGlobal, namespace { -void -AsyncLog(nsIInterceptedChannel *aInterceptedChannel, - const nsACString& aRespondWithScriptSpec, - uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, - const nsACString& aMessageName, const nsTArray& aParams) -{ - MOZ_ASSERT(aInterceptedChannel); - nsCOMPtr reporter = - aInterceptedChannel->GetConsoleReportCollector(); - if (reporter) { - reporter->AddConsoleReport(nsIScriptError::errorFlag, - NS_LITERAL_CSTRING("Service Worker Interception"), - nsContentUtils::eDOM_PROPERTIES, - aRespondWithScriptSpec, - aRespondWithLineNumber, - aRespondWithColumnNumber, - aMessageName, aParams); - } -} - -template -void -AsyncLog(nsIInterceptedChannel* aInterceptedChannel, - const nsACString& aRespondWithScriptSpec, - uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, - // We have to list one explicit string so that calls with an - // nsTArray of params won't end up in here. - const nsACString& aMessageName, const nsAString& aFirstParam, - Params&&... aParams) -{ - nsTArray paramsList(sizeof...(Params) + 1); - StringArrayAppender::Append(paramsList, sizeof...(Params) + 1, - aFirstParam, Forward(aParams)...); - AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber, - aRespondWithColumnNumber, aMessageName, paramsList); -} - class FinishResponse final : public nsRunnable { nsMainThreadPtrHandle mChannel; diff --git a/dom/workers/Location.cpp b/dom/workers/WorkerLocation.cpp similarity index 83% rename from dom/workers/Location.cpp rename to dom/workers/WorkerLocation.cpp index 30e8a2f295..469bf84bfd 100644 --- a/dom/workers/Location.cpp +++ b/dom/workers/WorkerLocation.cpp @@ -4,11 +4,12 @@ * 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 "Location.h" +#include "mozilla/dom/WorkerLocation.h" #include "mozilla/dom/WorkerLocationBinding.h" -BEGIN_WORKERS_NAMESPACE +namespace mozilla { +namespace dom { NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerLocation) @@ -16,7 +17,7 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerLocation, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerLocation, Release) /* static */ already_AddRefed -WorkerLocation::Create(WorkerPrivate::LocationInfo& aInfo) +WorkerLocation::Create(workers::WorkerPrivate::LocationInfo& aInfo) { RefPtr location = new WorkerLocation(NS_ConvertUTF8toUTF16(aInfo.mHref), @@ -35,7 +36,8 @@ WorkerLocation::Create(WorkerPrivate::LocationInfo& aInfo) JSObject* WorkerLocation::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { - return WorkerLocationBinding_workers::Wrap(aCx, this, aGivenProto); + return WorkerLocationBinding::Wrap(aCx, this, aGivenProto); } -END_WORKERS_NAMESPACE +} // namespace dom +} // namespace mozilla diff --git a/dom/workers/Location.h b/dom/workers/WorkerLocation.h similarity index 91% rename from dom/workers/Location.h rename to dom/workers/WorkerLocation.h index cf70001131..73137efff2 100644 --- a/dom/workers/Location.h +++ b/dom/workers/WorkerLocation.h @@ -4,14 +4,15 @@ * 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_workers_location_h__ -#define mozilla_dom_workers_location_h__ +#ifndef mozilla_dom_location_h__ +#define mozilla_dom_location_h__ #include "Workers.h" #include "WorkerPrivate.h" #include "nsWrapperCache.h" -BEGIN_WORKERS_NAMESPACE +namespace mozilla { +namespace dom { class WorkerLocation final : public nsWrapperCache { @@ -58,7 +59,7 @@ public: NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerLocation) static already_AddRefed - Create(WorkerPrivate::LocationInfo& aInfo); + Create(workers::WorkerPrivate::LocationInfo& aInfo); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -109,6 +110,7 @@ public: } }; -END_WORKERS_NAMESPACE +} // namespace dom +} // namespace mozilla -#endif // mozilla_dom_workers_location_h__ +#endif // mozilla_dom_location_h__ diff --git a/dom/workers/Navigator.cpp b/dom/workers/WorkerNavigator.cpp similarity index 98% rename from dom/workers/Navigator.cpp rename to dom/workers/WorkerNavigator.cpp index 825f51ff9e..b88dcd2bcf 100644 --- a/dom/workers/Navigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -11,9 +11,9 @@ #include "mozilla/dom/DataStoreBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseWorkerProxy.h" +#include "mozilla/dom/WorkerNavigator.h" #include "mozilla/dom/WorkerNavigatorBinding.h" -#include "Navigator.h" #include "nsProxyRelease.h" #include "RuntimeService.h" @@ -23,7 +23,8 @@ #include "WorkerRunnable.h" #include "WorkerScope.h" -BEGIN_WORKERS_NAMESPACE +namespace mozilla { +namespace dom { NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerNavigator) @@ -48,7 +49,7 @@ WorkerNavigator::Create(bool aOnLine) JSObject* WorkerNavigator::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { - return WorkerNavigatorBinding_workers::Wrap(aCx, this, aGivenProto); + return WorkerNavigatorBinding::Wrap(aCx, this, aGivenProto); } // A WorkerMainThreadRunnable to synchronously add DataStoreChangeEventProxy on @@ -300,7 +301,7 @@ WorkerNavigator::GetDataStores(JSContext* aCx, void WorkerNavigator::SetLanguages(const nsTArray& aLanguages) { - WorkerNavigatorBinding_workers::ClearCachedLanguagesValue(this); + WorkerNavigatorBinding::ClearCachedLanguagesValue(this); mProperties.mLanguages = aLanguages; } @@ -408,4 +409,5 @@ WorkerNavigator::HardwareConcurrency() const return rts->ClampedHardwareConcurrency(); } -END_WORKERS_NAMESPACE +} // namespace dom +} // namespace mozilla diff --git a/dom/workers/Navigator.h b/dom/workers/WorkerNavigator.h similarity index 91% rename from dom/workers/Navigator.h rename to dom/workers/WorkerNavigator.h index c4bdb6271d..b0ba074550 100644 --- a/dom/workers/Navigator.h +++ b/dom/workers/WorkerNavigator.h @@ -4,8 +4,8 @@ * 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_workers_navigator_h__ -#define mozilla_dom_workers_navigator_h__ +#ifndef mozilla_dom_workernavigator_h__ +#define mozilla_dom_workernavigator_h__ #include "Workers.h" #include "RuntimeService.h" @@ -19,14 +19,10 @@ namespace mozilla { namespace dom { class Promise; -} // namespace dom -} // namespace mozilla - -BEGIN_WORKERS_NAMESPACE class WorkerNavigator final : public nsWrapperCache { - typedef struct RuntimeService::NavigatorProperties NavigatorProperties; + typedef struct workers::RuntimeService::NavigatorProperties NavigatorProperties; NavigatorProperties mProperties; bool mOnline; @@ -116,6 +112,7 @@ public: uint64_t HardwareConcurrency() const; }; -END_WORKERS_NAMESPACE +} // namespace dom +} // namespace mozilla -#endif // mozilla_dom_workers_navigator_h__ +#endif // mozilla_dom_workernavigator_h__ diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index 8c98f3cc81..6b14cac308 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -21,6 +21,8 @@ #include "mozilla/dom/SharedWorkerGlobalScopeBinding.h" #include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h" #include "mozilla/dom/WorkerGlobalScopeBinding.h" +#include "mozilla/dom/WorkerLocation.h" +#include "mozilla/dom/WorkerNavigator.h" #include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/Services.h" #include "nsServiceManagerUtils.h" @@ -32,8 +34,6 @@ #include #endif -#include "Location.h" -#include "Navigator.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" @@ -696,9 +696,15 @@ WorkerDebuggerGlobalScope::WrapGlobalObject(JSContext* aCx, void WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx, - JS::MutableHandle aGlobal) + JS::MutableHandle aGlobal, + ErrorResult& aRv) { - aGlobal.set(mWorkerPrivate->GetOrCreateGlobalScope(aCx)->GetWrapper()); + WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); + if (!scope) { + aRv.Throw(NS_ERROR_FAILURE); + } + + aGlobal.set(scope->GetWrapper()); } class WorkerDebuggerSandboxPrivate : public nsIGlobalObject, @@ -931,7 +937,10 @@ void WorkerDebuggerGlobalScope::Dump(JSContext* aCx, const Optional& aString) const { - return mWorkerPrivate->GetOrCreateGlobalScope(aCx)->Dump(aString); + WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx); + if (scope) { + scope->Dump(aString); + } } nsIGlobalObject* diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 50de58e397..4ba8c06cc3 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -23,6 +23,8 @@ class IDBFactory; class Promise; class RequestOrUSVString; class ServiceWorkerRegistrationWorkerThread; +class WorkerLocation; +class WorkerNavigator; namespace cache { @@ -36,8 +38,6 @@ BEGIN_WORKERS_NAMESPACE class ServiceWorkerClients; class WorkerPrivate; -class WorkerLocation; -class WorkerNavigator; class Performance; class WorkerGlobalScope : public DOMEventTargetHelper, @@ -297,7 +297,8 @@ public: } void - GetGlobal(JSContext* aCx, JS::MutableHandle aGlobal); + GetGlobal(JSContext* aCx, JS::MutableHandle aGlobal, + ErrorResult& aRv); void CreateSandbox(JSContext* aCx, const nsAString& aName, diff --git a/dom/workers/moz.build b/dom/workers/moz.build index 9bbd500682..bd2a40d7f9 100644 --- a/dom/workers/moz.build +++ b/dom/workers/moz.build @@ -6,12 +6,15 @@ # Public stuff. EXPORTS.mozilla.dom += [ + 'FileReaderSync.h', 'ServiceWorkerCommon.h', 'ServiceWorkerContainer.h', 'ServiceWorkerEvents.h', 'ServiceWorkerMessageEvent.h', 'ServiceWorkerRegistrar.h', 'ServiceWorkerRegistration.h', + 'WorkerLocation.h', + 'WorkerNavigator.h', 'WorkerPrefs.h', 'WorkerPrivate.h', 'WorkerRunnable.h', @@ -29,9 +32,6 @@ EXPORTS.mozilla.dom.workers += [ EXPORTS.mozilla.dom.workers.bindings += [ 'DataStore.h', 'DataStoreCursor.h', - 'FileReaderSync.h', - 'Location.h', - 'Navigator.h', 'Performance.h', 'ServiceWorker.h', 'ServiceWorkerClient.h', @@ -56,8 +56,6 @@ UNIFIED_SOURCES += [ 'DataStore.cpp', 'DataStoreCursor.cpp', 'FileReaderSync.cpp', - 'Location.cpp', - 'Navigator.cpp', 'Performance.cpp', 'Principal.cpp', 'RegisterBindings.cpp', @@ -81,6 +79,8 @@ UNIFIED_SOURCES += [ 'SharedWorker.cpp', 'URL.cpp', 'WorkerDebuggerManager.cpp', + 'WorkerLocation.cpp', + 'WorkerNavigator.cpp', 'WorkerPrivate.cpp', 'WorkerRunnable.cpp', 'WorkerScope.cpp', diff --git a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js index ec71a93baf..12ffa42e0b 100644 --- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js +++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.js @@ -88,6 +88,8 @@ var interfaceNamesInGlobalScope = "Client", // IMPORTANT: Do not change this list without review from a DOM peer! "Clients", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CustomEvent", // IMPORTANT: Do not change this list without review from a DOM peer! { name: "DataStore", b2g: true }, // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/dom/workers/test/test_worker_interfaces.js b/dom/workers/test/test_worker_interfaces.js index b3892dd2b8..0adc1ccccf 100644 --- a/dom/workers/test/test_worker_interfaces.js +++ b/dom/workers/test/test_worker_interfaces.js @@ -84,6 +84,8 @@ var interfaceNamesInGlobalScope = "Cache", // IMPORTANT: Do not change this list without review from a DOM peer! "CacheStorage", +// IMPORTANT: Do not change this list without review from a DOM peer! + "CustomEvent", // IMPORTANT: Do not change this list without review from a DOM peer! "DedicatedWorkerGlobalScope", // IMPORTANT: Do not change this list without review from a DOM peer! diff --git a/layout/generic/nsBRFrame.cpp b/layout/generic/nsBRFrame.cpp index b4695ef28e..2d7ec6487e 100644 --- a/layout/generic/nsBRFrame.cpp +++ b/layout/generic/nsBRFrame.cpp @@ -266,6 +266,13 @@ BRFrame::AccessibleType() return a11y::eNoType; } + // Trailing HTML br element don't play any difference. We don't need to expose + // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16 + // for details). + if (!mContent->GetNextSibling() && !GetNextSibling()) { + return a11y::eNoType; + } + return a11y::eHTMLBRType; } #endif diff --git a/layout/style/AnimationCollection.cpp b/layout/style/AnimationCollection.cpp index 90d6ec28b2..f7826f5ed3 100644 --- a/layout/style/AnimationCollection.cpp +++ b/layout/style/AnimationCollection.cpp @@ -63,18 +63,18 @@ template AnimationCollection::GetAnimationCollection( const nsIFrame* aFrame) { - Maybe> pseudoElement = + Maybe pseudoElement = EffectCompositor::GetAnimationElementAndPseudoForFrame(aFrame); if (!pseudoElement) { return nullptr; } - if (!pseudoElement->first()->MayHaveAnimations()) { + if (!pseudoElement->mElement->MayHaveAnimations()) { return nullptr; } - return GetAnimationCollection(pseudoElement->first(), - pseudoElement->second()); + return GetAnimationCollection(pseudoElement->mElement, + pseudoElement->mPseudoType); } template diff --git a/modules/libjar/test/unit/test_jarchannel.js b/modules/libjar/test/unit/test_jarchannel.js index 530ac65f45..49421b14c2 100644 --- a/modules/libjar/test/unit/test_jarchannel.js +++ b/modules/libjar/test/unit/test_jarchannel.js @@ -310,4 +310,6 @@ if (inChild) { }); } // if inChild -function run_test() run_next_test(); +function run_test() { + return run_next_test(); +} diff --git a/netwerk/test/browser/browser_NetUtil.js b/netwerk/test/browser/browser_NetUtil.js index 974a7e1feb..a6c4f2bcd6 100644 --- a/netwerk/test/browser/browser_NetUtil.js +++ b/netwerk/test/browser/browser_NetUtil.js @@ -42,7 +42,7 @@ function test_asyncFetchBadCert() { channel.notificationCallbacks = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIProgressEventSink, Ci.nsIInterfaceRequestor]), - getInterface: function (aIID) this.QueryInterface(aIID), + getInterface: function (aIID) { return this.QueryInterface(aIID); }, onProgress: function () {}, onStatus: function () {} }; diff --git a/netwerk/test/httpserver/test/test_registerdirectory.js b/netwerk/test/httpserver/test/test_registerdirectory.js index 3894413068..fbb41293e9 100644 --- a/netwerk/test/httpserver/test/test_registerdirectory.js +++ b/netwerk/test/httpserver/test/test_registerdirectory.js @@ -45,7 +45,7 @@ function checkFile(ch, cx, status, data) actualFile.append("test_registerdirectory.js"); do_check_eq(ch.getResponseHeader("Content-Length"), actualFile.fileSize.toString()); - do_check_eq(data.map(function(v) String.fromCharCode(v)).join(""), + do_check_eq(data.map(v => String.fromCharCode(v)).join(""), fileContents(actualFile)); } diff --git a/netwerk/test/unit/test_bug894586.js b/netwerk/test/unit/test_bug894586.js index 080adb0afa..26e945839b 100644 --- a/netwerk/test/unit/test_bug894586.js +++ b/netwerk/test/unit/test_bug894586.js @@ -18,28 +18,55 @@ function ProtocolHandler() { ProtocolHandler.prototype = { /** nsIProtocolHandler */ - get scheme() "x-bug894586", - get defaultPort() -1, - get protocolFlags() Ci.nsIProtocolHandler.URI_NORELATIVE | - Ci.nsIProtocolHandler.URI_NOAUTH | - Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE | - Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE | - Ci.nsIProtocolHandler.URI_NON_PERSISTABLE | - Ci.nsIProtocolHandler.URI_SYNC_LOAD_IS_OK, - newURI: function(aSpec, aOriginCharset, aBaseURI) this.uri, - newChannel: function(aURI) this, - allowPort: function(port, scheme) port != -1, + get scheme() { + return "x-bug894586"; + }, + get defaultPort() { + return -1; + }, + get protocolFlags() { + return Ci.nsIProtocolHandler.URI_NORELATIVE | + Ci.nsIProtocolHandler.URI_NOAUTH | + Ci.nsIProtocolHandler.URI_IS_UI_RESOURCE | + Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE | + Ci.nsIProtocolHandler.URI_NON_PERSISTABLE | + Ci.nsIProtocolHandler.URI_SYNC_LOAD_IS_OK; + }, + newURI: function(aSpec, aOriginCharset, aBaseURI) { + return this.uri; + }, + newChannel2: function(aURI, aLoadInfo) { + this.loadInfo = aLoadInfo; + return this; + }, + newChannel: function(aURI) { + return this; + }, + allowPort: function(port, scheme) { + return port != -1; + }, /** nsIChannel */ - get originalURI() this.uri, - get URI() this.uri, + get originalURI() { + return this.uri; + }, + get URI() { + return this.uri; + }, owner: null, notificationCallbacks: null, - get securityInfo() null, - get contentType() "text/css", - set contentType(val) void(0), + get securityInfo() { + return null; + }, + get contentType() { + return "text/css"; + }, + set contentType(val) { + }, contentCharset: "UTF-8", - get contentLength() -1, + get contentLength() { + return -1; + }, set contentLength(val) { throw Components.Exception("Setting content length", NS_ERROR_NOT_IMPLEMENTED); }, @@ -78,9 +105,13 @@ ProtocolHandler.prototype = { }, /** nsIRequest */ - get name() this.uri.spec, - isPending: function() false, - get status() Cr.NS_OK, + get name() { + return this.uri.spec; + }, + isPending: () => false, + get status() { + return Cr.NS_OK; + }, cancel: function(status) {}, loadGroup: null, loadFlags: Ci.nsIRequest.LOAD_NORMAL | diff --git a/netwerk/test/unit/test_protocolproxyservice.js b/netwerk/test/unit/test_protocolproxyservice.js index b08865ee40..da7ef7f8bb 100644 --- a/netwerk/test/unit/test_protocolproxyservice.js +++ b/netwerk/test/unit/test_protocolproxyservice.js @@ -51,6 +51,9 @@ TestProtocolHandler.prototype = { uri.spec = spec; return uri; }, + newChannel2: function(uri, aLoadInfo) { + throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + }, newChannel: function(uri) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, diff --git a/parser/xml/test/unit/results.js b/parser/xml/test/unit/results.js index 3dbaa421e5..6de6292c13 100644 --- a/parser/xml/test/unit/results.js +++ b/parser/xml/test/unit/results.js @@ -14,7 +14,7 @@ var vectors = [ "sanitized": "&alert&A7&(1)&R&UA;&&<&A9&11/script&X&>" }, { - "data": "0? :postMessage(importScripts('data:;base64,cG9zdE1lc3NhZ2UoJ2FsZXJ0KDEpJyk'))", + "data": "0? :postMessage(importScripts('data:;base64,cG9zdE1lc3NhZ2UoJ2FsZXJ0KDEpJyk'))", "sanitized": "0? :postMessage(importScripts('data:;base64,cG9zdE1lc3NhZ2UoJ2FsZXJ0KDEpJyk'))" }, { diff --git a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js index 83b0981eee..7421ae0bd2 100644 --- a/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js +++ b/security/manager/ssl/tests/mochitest/browser/browser_certViewer.js @@ -29,8 +29,12 @@ function test() { ok(cert, "found a certificate to look at"); info("looking at certificate with nickname " + cert.nickname); let arg = { - QueryInterface: function() this, - getISupportAtIndex: function() this.cert, + QueryInterface: function() { + return this; + }, + getISupportAtIndex: function() { + return this.cert; + }, cert: cert }; gBugWindow = window.openDialog("chrome://pippki/content/certViewer.xul", diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index e64b1f293c..a94ece76a0 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -18883,6 +18883,10 @@ "path": "web-animations/keyframe-effect/getComputedTiming.html", "url": "/web-animations/keyframe-effect/getComputedTiming.html" }, + { + "path": "web-animations/keyframe-effect/keyframe-handling.html", + "url": "/web-animations/keyframe-effect/keyframe-handling.html" + }, { "path": "webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html", "url": "/webaudio/the-audio-api/the-audiobuffer-interface/idl-test.html" diff --git a/testing/web-platform/tests/web-animations/keyframe-effect/keyframe-handling.html b/testing/web-platform/tests/web-animations/keyframe-effect/keyframe-handling.html new file mode 100644 index 0000000000..9eac4711f1 --- /dev/null +++ b/testing/web-platform/tests/web-animations/keyframe-effect/keyframe-handling.html @@ -0,0 +1,76 @@ + + +Keyframe handling tests + + + + + + + +
+
+ + diff --git a/tools/jprof/split-profile.py b/tools/jprof/split-profile.py index c9f1b43004..89454d3eb9 100755 --- a/tools/jprof/split-profile.py +++ b/tools/jprof/split-profile.py @@ -93,7 +93,14 @@ def generate_profile(options, destfile): args = [jprof] + options + passthrough print "Generating {0}".format(destfile) destio = open(destfile, "w") - process = subprocess.Popen(args, stdout=destio) + # jprof expects the "jprof-map" file to be in its current working directory + cwd = None + for option in passthrough: + if option.find("jprof-log"): + cwd = os.path.dirname(option) + if cwd is None: + raise StandardError("no jprof-log option given") + process = subprocess.Popen(args, stdout=destio, cwd=cwd) process.wait() destio.close() if process.returncode != 0: diff --git a/widget/tests/bug586713_window.xul b/widget/tests/bug586713_window.xul index a50996128c..78397afad1 100644 --- a/widget/tests/bug586713_window.xul +++ b/widget/tests/bug586713_window.xul @@ -44,7 +44,7 @@ function onLoad() { foo(); - setTimeout(function() foo(), 0); + setTimeout(() => foo(), 0); } ]]> diff --git a/widget/tests/test_bug760802.xul b/widget/tests/test_bug760802.xul index 0a5fc5fe67..c5de5936c6 100644 --- a/widget/tests/test_bug760802.xul +++ b/widget/tests/test_bug760802.xul @@ -51,7 +51,9 @@ function shouldThrowException(fun, exception) { e.result === exception) } } -function doesntThrowException(fun) !shouldThrowException(fun) +function doesntThrowException(fun) { + return !shouldThrowException(fun); +} var baseWindow = getBaseWindowInterface(this); var nativeHandle = baseWindow.nativeHandle; diff --git a/widget/tests/test_taskbar_progress.xul b/widget/tests/test_taskbar_progress.xul index 56f8005ce0..b08e9803d9 100644 --- a/widget/tests/test_taskbar_progress.xul +++ b/widget/tests/test_taskbar_progress.xul @@ -67,7 +67,9 @@ } } - function doesntThrow(s,c,m) !shouldThrow(s,c,m) + function doesntThrow(s,c,m) { + return !shouldThrow(s,c,m); + } ok(doesntThrow(TP.STATE_NO_PROGRESS, 0, 0), "No progress state can be set"); ok(doesntThrow(TP.STATE_INDETERMINATE, 0, 0), "Indeterminate state can be set"); diff --git a/xpcom/tests/unit/test_symlinks.js b/xpcom/tests/unit/test_symlinks.js index adc5afeb3d..865639dd78 100644 --- a/xpcom/tests/unit/test_symlinks.js +++ b/xpcom/tests/unit/test_symlinks.js @@ -109,7 +109,9 @@ function setupTestDir(testDir, relative) { } function createSpaces(dirs, files, links) { - function longest(a, b) a.length > b.length ? a : b; + function longest(a, b) { + return a.length > b.length ? a : b; + } return dirs.concat(files, links).reduce(longest, "").replace(/./g, " "); }