diff -Nru firefox-84.0.2+build1/accessible/android/DocAccessibleWrap.cpp firefox-85.0+build1/accessible/android/DocAccessibleWrap.cpp --- firefox-84.0.2+build1/accessible/android/DocAccessibleWrap.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/android/DocAccessibleWrap.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -102,7 +102,7 @@ nsLayoutUtils::GetFramesForArea( RelativeTo{presShell->GetRootFrame()}, scrollPort, frames, - nsLayoutUtils::FrameForPointOption::OnlyVisible); + {nsLayoutUtils::FrameForPointOption::OnlyVisible}); AccessibleHashtable inViewAccs; for (size_t i = 0; i < frames.Length(); i++) { nsIContent* content = frames.ElementAt(i)->GetContent(); diff -Nru firefox-84.0.2+build1/accessible/android/RootAccessibleWrap.cpp firefox-85.0+build1/accessible/android/RootAccessibleWrap.cpp --- firefox-84.0.2+build1/accessible/android/RootAccessibleWrap.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/android/RootAccessibleWrap.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -8,6 +8,7 @@ #include "Accessible-inl.h" #include "AccessibleOrProxy.h" #include "DocAccessibleParent.h" +#include "DocAccessible-inl.h" #include "ProxyAccessibleWrap.h" #include "SessionAccessibility.h" #include "mozilla/PresShell.h" diff -Nru firefox-84.0.2+build1/accessible/aom/AccessibleNode.cpp firefox-85.0+build1/accessible/aom/AccessibleNode.cpp --- firefox-84.0.2+build1/accessible/aom/AccessibleNode.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/aom/AccessibleNode.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -8,6 +8,7 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/DOMStringList.h" #include "mozilla/StaticPrefs_accessibility.h" +#include "nsContentUtils.h" #include "nsIPersistentProperties2.h" #include "nsISimpleEnumerator.h" @@ -15,6 +16,9 @@ #include "nsAccessibilityService.h" #include "DocAccessible.h" +#include "mozilla/dom/Document.h" // for inline nsINode::GetParentObject +#include "mozilla/dom/ToJSValue.h" + using namespace mozilla; using namespace mozilla::a11y; using namespace mozilla::dom; diff -Nru firefox-84.0.2+build1/accessible/aom/AccessibleNode.h firefox-85.0+build1/accessible/aom/AccessibleNode.h --- firefox-84.0.2+build1/accessible/aom/AccessibleNode.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/aom/AccessibleNode.h 2021-01-18 19:58:08.000000000 +0000 @@ -10,14 +10,16 @@ #include "nsDataHashtable.h" #include "nsRefPtrHashtable.h" #include "nsWrapperCache.h" -#include "mozilla/ErrorResult.h" #include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DOMString.h" #include "mozilla/dom/Nullable.h" class nsINode; namespace mozilla { +class ErrorResult; + namespace a11y { class Accessible; } diff -Nru firefox-84.0.2+build1/accessible/atk/Platform.cpp firefox-85.0+build1/accessible/atk/Platform.cpp --- firefox-84.0.2+build1/accessible/atk/Platform.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/atk/Platform.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -9,6 +9,7 @@ #include "nsIAccessibleEvent.h" #include "nsIGSettingsService.h" #include "nsMai.h" +#include "nsServiceManagerUtils.h" #include "AtkSocketAccessible.h" #include "prenv.h" #include "prlink.h" diff -Nru firefox-84.0.2+build1/accessible/base/AccEvent.cpp firefox-85.0+build1/accessible/base/AccEvent.cpp --- firefox-84.0.2+build1/accessible/base/AccEvent.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/AccEvent.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -17,6 +17,7 @@ #include "mozilla/dom/Selection.h" #include "mozilla/dom/UserActivation.h" +#include "nsComponentManagerUtils.h" #include "nsIMutableArray.h" using namespace mozilla; diff -Nru firefox-84.0.2+build1/accessible/base/AccIterator.cpp firefox-85.0+build1/accessible/base/AccIterator.cpp --- firefox-84.0.2+build1/accessible/base/AccIterator.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/AccIterator.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -9,6 +9,7 @@ # include "XULTreeAccessible.h" #endif +#include "mozilla/dom/DocumentOrShadowRoot.h" #include "mozilla/dom/HTMLLabelElement.h" using namespace mozilla; diff -Nru firefox-84.0.2+build1/accessible/base/ARIAMap.cpp firefox-85.0+build1/accessible/base/ARIAMap.cpp --- firefox-84.0.2+build1/accessible/base/ARIAMap.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/ARIAMap.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -43,7 +43,11 @@ kUseMapRole, eNoValue, eNoAction, +#if defined(XP_MACOSX) + eAssertiveLiveAttr, +#else eNoLiveAttr, +#endif eAlert, kNoReqStates }, @@ -1462,6 +1466,11 @@ //////////////////////////////////////////////////////////////////////////////// // AttrIterator class +AttrIterator::AttrIterator(nsIContent* aContent) + : mElement(dom::Element::FromNode(aContent)), mAttrIdx(0) { + mAttrCount = mElement ? mElement->GetAttrCount() : 0; +} + bool AttrIterator::Next(nsAString& aAttrName, nsAString& aAttrValue) { while (mAttrIdx < mAttrCount) { const nsAttrName* attr = mElement->GetAttrNameAt(mAttrIdx); diff -Nru firefox-84.0.2+build1/accessible/base/ARIAMap.h firefox-85.0+build1/accessible/base/ARIAMap.h --- firefox-84.0.2+build1/accessible/base/ARIAMap.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/ARIAMap.h 2021-01-18 19:58:08.000000000 +0000 @@ -14,10 +14,13 @@ #include "nsAtom.h" #include "nsIContent.h" -#include "mozilla/dom/Element.h" class nsINode; +namespace mozilla::dom { +class Element; +} + //////////////////////////////////////////////////////////////////////////////// // Value constants @@ -71,7 +74,12 @@ /** * Used to define if role exposes default value of aria-live attribute. */ -enum ELiveAttrRule { eNoLiveAttr, eOffLiveAttr, ePoliteLiveAttr }; +enum ELiveAttrRule { + eNoLiveAttr, + eOffLiveAttr, + ePoliteLiveAttr, + eAssertiveLiveAttr +}; //////////////////////////////////////////////////////////////////////////////// // Role constants @@ -276,10 +284,7 @@ */ class AttrIterator { public: - explicit AttrIterator(nsIContent* aContent) - : mElement(dom::Element::FromNode(aContent)), mAttrIdx(0) { - mAttrCount = mElement ? mElement->GetAttrCount() : 0; - } + explicit AttrIterator(nsIContent* aContent); bool Next(nsAString& aAttrName, nsAString& aAttrValue); diff -Nru firefox-84.0.2+build1/accessible/base/DocManager.cpp firefox-85.0+build1/accessible/base/DocManager.cpp --- firefox-84.0.2+build1/accessible/base/DocManager.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/DocManager.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -62,6 +62,19 @@ return CreateDocOrRootAccessible(aDocument); } +DocAccessible* DocManager::GetDocAccessible(const PresShell* aPresShell) { + if (!aPresShell) { + return nullptr; + } + + DocAccessible* doc = aPresShell->GetDocAccessible(); + if (doc) { + return doc; + } + + return GetDocAccessible(aPresShell->GetDocument()); +} + Accessible* DocManager::FindAccessibleInCache(nsINode* aNode) const { for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) { DocAccessible* docAccessible = iter.UserData(); @@ -541,3 +554,9 @@ sRemoteDocuments->AppendElement(aDoc); ProxyCreated(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT); } + +DocAccessible* mozilla::a11y::GetExistingDocAccessible( + const dom::Document* aDocument) { + PresShell* presShell = aDocument->GetPresShell(); + return presShell ? presShell->GetDocAccessible() : nullptr; +} diff -Nru firefox-84.0.2+build1/accessible/base/DocManager.h firefox-85.0+build1/accessible/base/DocManager.h --- firefox-84.0.2+build1/accessible/base/DocManager.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/DocManager.h 2021-01-18 19:58:08.000000000 +0000 @@ -6,15 +6,20 @@ #define mozilla_a11_DocManager_h_ #include "mozilla/ClearOnShutdown.h" -#include "mozilla/PresShell.h" -#include "mozilla/dom/Document.h" #include "nsIDOMEventListener.h" #include "nsRefPtrHashtable.h" #include "nsIWebProgressListener.h" #include "nsWeakReference.h" #include "mozilla/StaticPtr.h" +#include "nsINode.h" + +namespace mozilla::dom { +class Document; +} namespace mozilla { +class PresShell; + namespace a11y { class Accessible; @@ -41,18 +46,7 @@ /** * Return document accessible for the given presshell. */ - DocAccessible* GetDocAccessible(const PresShell* aPresShell) { - if (!aPresShell) { - return nullptr; - } - - DocAccessible* doc = aPresShell->GetDocAccessible(); - if (doc) { - return doc; - } - - return GetDocAccessible(aPresShell->GetDocument()); - } + DocAccessible* GetDocAccessible(const PresShell* aPresShell); /** * Search through all document accessibles for an accessible with the given @@ -187,10 +181,7 @@ * Note this returns the doc accessible for the primary pres shell if there is * more than one. */ -inline DocAccessible* GetExistingDocAccessible(const dom::Document* aDocument) { - PresShell* presShell = aDocument->GetPresShell(); - return presShell ? presShell->GetDocAccessible() : nullptr; -} +DocAccessible* GetExistingDocAccessible(const dom::Document* aDocument); } // namespace a11y } // namespace mozilla diff -Nru firefox-84.0.2+build1/accessible/base/NotificationController.cpp firefox-85.0+build1/accessible/base/NotificationController.cpp --- firefox-84.0.2+build1/accessible/base/NotificationController.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/NotificationController.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -7,6 +7,7 @@ #include "DocAccessible-inl.h" #include "DocAccessibleChild.h" +#include "GeckoProfiler.h" #include "nsEventShell.h" #include "TextLeafAccessible.h" #include "TextUpdater.h" diff -Nru firefox-84.0.2+build1/accessible/base/nsAccessibilityService.cpp firefox-85.0+build1/accessible/base/nsAccessibilityService.cpp --- firefox-84.0.2+build1/accessible/base/nsAccessibilityService.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/nsAccessibilityService.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -27,6 +27,7 @@ #include "nsDOMTokenList.h" #include "nsEventShell.h" #include "nsIFrameInlines.h" +#include "nsServiceManagerUtils.h" #include "nsTextFormatter.h" #include "OuterDocAccessible.h" #include "Role.h" diff -Nru firefox-84.0.2+build1/accessible/base/nsAccessibilityService.h firefox-85.0+build1/accessible/base/nsAccessibilityService.h --- firefox-84.0.2+build1/accessible/base/nsAccessibilityService.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/nsAccessibilityService.h 2021-01-18 19:58:08.000000000 +0000 @@ -13,9 +13,11 @@ #include "mozilla/a11y/SelectionManager.h" #include "mozilla/Preferences.h" +#include "nsIContent.h" #include "nsIObserver.h" #include "nsIAccessibleEvent.h" #include "nsIEventListenerService.h" +#include "nsXULAppAPI.h" #include "xpcAccessibilityService.h" class nsImageFrame; @@ -498,6 +500,8 @@ "text value change", // EVENT_TEXT_VALUE_CHANGE "scrolling", // EVENT_SCROLLING "announcement", // EVENT_ANNOUNCEMENT + "live region added", // EVENT_LIVE_REGION_ADDED + "live region removed", // EVENT_LIVE_REGION_REMOVED }; #endif diff -Nru firefox-84.0.2+build1/accessible/base/nsAccUtils.cpp firefox-85.0+build1/accessible/base/nsAccUtils.cpp --- firefox-84.0.2+build1/accessible/base/nsAccUtils.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/nsAccUtils.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -18,6 +18,7 @@ #include "nsIDOMXULContainerElement.h" #include "nsIPersistentProperties2.h" +#include "nsISimpleEnumerator.h" #include "mozilla/a11y/PDocAccessibleChild.h" #include "mozilla/dom/Element.h" #include "nsAccessibilityService.h" @@ -361,6 +362,9 @@ case ePoliteLiveAttr: aValue = u"polite"_ns; return true; + case eAssertiveLiveAttr: + aValue = u"assertive"_ns; + return true; } return false; diff -Nru firefox-84.0.2+build1/accessible/base/nsCoreUtils.cpp firefox-85.0+build1/accessible/base/nsCoreUtils.cpp --- firefox-84.0.2+build1/accessible/base/nsCoreUtils.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/nsCoreUtils.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -383,6 +383,10 @@ return StringBeginsWith(path, neterror) || StringBeginsWith(path, certerror); } +PresShell* nsCoreUtils::GetPresShellFor(nsINode* aNode) { + return aNode->OwnerDoc()->GetPresShell(); +} + bool nsCoreUtils::GetID(nsIContent* aContent, nsAString& aID) { return aContent->IsElement() && aContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aID); @@ -538,6 +542,12 @@ ScrollFlags::ScrollOverflowHidden); } +bool nsCoreUtils::IsHTMLTableHeader(nsIContent* aContent) { + return aContent->NodeInfo()->Equals(nsGkAtoms::th) || + (aContent->IsElement() && + aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::scope)); +} + bool nsCoreUtils::IsWhitespaceString(const nsAString& aString) { nsAString::const_char_iterator iterBegin, iterEnd; diff -Nru firefox-84.0.2+build1/accessible/base/nsCoreUtils.h firefox-85.0+build1/accessible/base/nsCoreUtils.h --- firefox-84.0.2+build1/accessible/base/nsCoreUtils.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/nsCoreUtils.h 2021-01-18 19:58:08.000000000 +0000 @@ -7,10 +7,8 @@ #define nsCoreUtils_h_ #include "mozilla/EventForwards.h" -#include "mozilla/dom/Element.h" #include "nsIAccessibleEvent.h" #include "nsIContent.h" -#include "mozilla/dom/Document.h" // for GetPresShell() #include "mozilla/FlushType.h" #include "mozilla/PresShellForwards.h" @@ -26,8 +24,9 @@ namespace mozilla { class PresShell; namespace dom { +class Document; class XULTreeElement; -} +} // namespace dom } // namespace mozilla /** @@ -219,9 +218,7 @@ /** * Return presShell for the document containing the given DOM node. */ - static PresShell* GetPresShellFor(nsINode* aNode) { - return aNode->OwnerDoc()->GetPresShell(); - } + static PresShell* GetPresShellFor(nsINode* aNode); /** * Get the ID for an element, in some types of XML this may not be the ID @@ -298,11 +295,7 @@ /** * Return true if the given node is table header element. */ - static bool IsHTMLTableHeader(nsIContent* aContent) { - return aContent->NodeInfo()->Equals(nsGkAtoms::th) || - (aContent->IsElement() && aContent->AsElement()->HasAttr( - kNameSpaceID_None, nsGkAtoms::scope)); - } + static bool IsHTMLTableHeader(nsIContent* aContent); /** * Returns true if the given string is empty or contains whitespace symbols diff -Nru firefox-84.0.2+build1/accessible/base/Platform.h firefox-85.0+build1/accessible/base/Platform.h --- firefox-84.0.2+build1/accessible/base/Platform.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/base/Platform.h 2021-01-18 19:58:07.000000000 +0000 @@ -19,6 +19,10 @@ # include "mozilla/a11y/Role.h" #endif +#if defined(XP_WIN) +# include "Units.h" +#endif + namespace mozilla { namespace a11y { diff -Nru firefox-84.0.2+build1/accessible/generic/Accessible.cpp firefox-85.0+build1/accessible/generic/Accessible.cpp --- firefox-84.0.2+build1/accessible/generic/Accessible.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/Accessible.cpp 2021-01-18 19:58:08.000000000 +0000 @@ -709,8 +709,7 @@ } } - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - if (fm) { + if (RefPtr fm = nsFocusManager::GetFocusManager()) { dom::AutoHandlingUserInputStatePusher inputStatePusher(true); // XXXbz: Can we actually have a non-element content here? RefPtr element = dom::Element::FromNodeOrNull(focusContent); @@ -2130,6 +2129,10 @@ nsINode* Accessible::GetNode() const { return mContent; } +dom::Element* Accessible::Elm() const { + return dom::Element::FromNodeOrNull(mContent); +} + void Accessible::Language(nsAString& aLanguage) { aLanguage.Truncate(); diff -Nru firefox-84.0.2+build1/accessible/generic/Accessible.h firefox-85.0+build1/accessible/generic/Accessible.h --- firefox-84.0.2+build1/accessible/generic/Accessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/Accessible.h 2021-01-18 19:58:08.000000000 +0000 @@ -11,8 +11,6 @@ #include "mozilla/a11y/Role.h" #include "mozilla/a11y/States.h" -#include "mozilla/dom/Element.h" - #include "mozilla/UniquePtr.h" #include "nsIContent.h" @@ -25,6 +23,10 @@ class nsIFrame; class nsIPersistentProperties; +namespace mozilla::dom { +class Element; +} + namespace mozilla { namespace a11y { @@ -161,7 +163,7 @@ virtual nsINode* GetNode() const; nsIContent* GetContent() const { return mContent; } - dom::Element* Elm() const { return dom::Element::FromNodeOrNull(mContent); } + dom::Element* Elm() const; /** * Return node type information of DOM node associated with the accessible. @@ -519,7 +521,7 @@ /** * Focus the accessible. */ - virtual void TakeFocus() const; + MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual void TakeFocus() const; /** * Scroll the accessible into view. diff -Nru firefox-84.0.2+build1/accessible/generic/Accessible-inl.h firefox-85.0+build1/accessible/generic/Accessible-inl.h --- firefox-84.0.2+build1/accessible/generic/Accessible-inl.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/Accessible-inl.h 2021-01-18 19:58:08.000000000 +0000 @@ -10,6 +10,7 @@ #include "DocAccessible.h" #include "ARIAMap.h" #include "nsCoreUtils.h" +#include "mozilla/dom/Element.h" #include "mozilla/PresShell.h" #ifdef A11Y_LOG diff -Nru firefox-84.0.2+build1/accessible/generic/ARIAGridAccessible.cpp firefox-85.0+build1/accessible/generic/ARIAGridAccessible.cpp --- firefox-84.0.2+build1/accessible/generic/ARIAGridAccessible.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/ARIAGridAccessible.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -11,6 +11,7 @@ #include "Role.h" #include "States.h" +#include "mozilla/dom/Element.h" #include "nsIPersistentProperties2.h" #include "nsComponentManagerUtils.h" diff -Nru firefox-84.0.2+build1/accessible/generic/DocAccessible.cpp firefox-85.0+build1/accessible/generic/DocAccessible.cpp --- firefox-84.0.2+build1/accessible/generic/DocAccessible.cpp 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/DocAccessible.cpp 2021-01-18 19:58:07.000000000 +0000 @@ -338,6 +338,14 @@ CopyUTF8toUTF16(theURL, aURL); } +void DocAccessible::Title(nsString& aTitle) const { + mDocumentNode->GetTitle(aTitle); +} + +void DocAccessible::MimeType(nsAString& aType) const { + mDocumentNode->GetContentType(aType); +} + void DocAccessible::DocType(nsAString& aType) const { dom::DocumentType* docType = mDocumentNode->GetDoctype(); if (docType) docType->GetPublicId(aType); @@ -449,6 +457,8 @@ return root; } +nsINode* DocAccessible::GetNode() const { return mDocumentNode; } + // DocAccessible protected member nsRect DocAccessible::RelativeBounds(nsIFrame** aRelativeFrame) const { *aRelativeFrame = GetFrame(); @@ -1177,6 +1187,10 @@ //////////////////////////////////////////////////////////////////////////////// // Public members +nsPresContext* DocAccessible::PresContext() const { + return mPresShell->GetPresContext(); +} + void* DocAccessible::GetNativeWindow() const { if (!mPresShell) { return nullptr; @@ -1911,6 +1925,8 @@ bool InsertIterator::Next() { if (mNodesIdx > 0) { + // If we already processed the first node in the mNodes list, + // check if we can just use the walker to get its next sibling. Accessible* nextChild = mWalker.Next(); if (nextChild) { mChildBefore = mChild; @@ -1920,16 +1936,6 @@ } while (mNodesIdx < mNodes->Length()) { - // Ignore nodes that are not contained by the container anymore. - - // The container might be changed, for example, because of the subsequent - // overlapping content insertion (i.e. other content was inserted between - // this inserted content and its container or the content was reinserted - // into different container of unrelated part of tree). To avoid a double - // processing of the content insertion ignore this insertion notification. - // Note, the inserted content might be not in tree at all at this point - // what means there's no container. Ignore the insertion too. - nsIContent* prevNode = mNodes->SafeElementAt(mNodesIdx - 1); nsIContent* node = mNodes->ElementAt(mNodesIdx++); // Check to see if we already processed this node with this iterator. // this can happen if we get two redundant insertions in the case of a @@ -1940,6 +1946,14 @@ Accessible* container = Document()->AccessibleOrTrueContainer( node->GetFlattenedTreeParentNode(), true); + // Ignore nodes that are not contained by the container anymore. + // The container might be changed, for example, because of the subsequent + // overlapping content insertion (i.e. other content was inserted between + // this inserted content and its container or the content was reinserted + // into different container of unrelated part of tree). To avoid a double + // processing of the content insertion ignore this insertion notification. + // Note, the inserted content might be not in tree at all at this point + // what means there's no container. Ignore the insertion too. if (container != Context()) { continue; } @@ -1959,8 +1973,9 @@ "container", container, "node", node); #endif - // If inserted nodes are siblings then just move the walker next. - if (mChild && prevNode && prevNode->GetNextSibling() == node) { + nsIContent* prevNode = mChild ? mChild->GetContent() : nullptr; + if (prevNode && prevNode->GetNextSibling() == node) { + // If inserted nodes are siblings then just move the walker next. Accessible* nextChild = mWalker.Scope(node); if (nextChild) { mChildBefore = mChild; @@ -1968,6 +1983,8 @@ return true; } } else { + // Otherwise use a new walker to find this node in the container's + // subtree, and retrieve its preceding sibling. TreeWalker finder(container); if (finder.Seek(node)) { mChild = mWalker.Scope(node); @@ -2676,3 +2693,8 @@ // No other ARIA roles are valid on body elements. SetRoleMapEntry(nullptr); } + +Accessible* DocAccessible::GetAccessible(nsINode* aNode) const { + return aNode == mDocumentNode ? const_cast(this) + : mNodeToAccessibleMap.Get(aNode); +} diff -Nru firefox-84.0.2+build1/accessible/generic/DocAccessible.h firefox-85.0+build1/accessible/generic/DocAccessible.h --- firefox-84.0.2+build1/accessible/generic/DocAccessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/DocAccessible.h 2021-01-18 19:58:07.000000000 +0000 @@ -13,11 +13,11 @@ #include "nsClassHashtable.h" #include "nsDataHashtable.h" -#include "mozilla/dom/Document.h" #include "mozilla/UniquePtr.h" #include "nsIDocumentObserver.h" #include "nsIObserver.h" #include "nsITimer.h" +#include "nsWeakReference.h" class nsAccessiblePivot; @@ -65,7 +65,7 @@ virtual void Init(); virtual void Shutdown() override; virtual nsIFrame* GetFrame() const override; - virtual nsINode* GetNode() const override { return mDocumentNode; } + virtual nsINode* GetNode() const override; Document* DocumentNode() const { return mDocumentNode; } virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) const override; @@ -99,15 +99,12 @@ /** * Return DOM document title. */ - void Title(nsString& aTitle) const { mDocumentNode->GetTitle(aTitle); } + void Title(nsString& aTitle) const; /** * Return DOM document mime type. */ - void MimeType(nsAString& aType) const { - mDocumentNode->GetContentType(aType); - } - + void MimeType(nsAString& aType) const; /** * Return DOM document type. */ @@ -134,20 +131,14 @@ /** * Return the presentation shell's context. */ - nsPresContext* PresContext() const { return mPresShell->GetPresContext(); } + nsPresContext* PresContext() const; /** * Return true if associated DOM document was loaded and isn't unloading. */ - bool IsContentLoaded() const { - // eDOMLoaded flag check is used for error pages as workaround to make this - // method return correct result since error pages do not receive 'pageshow' - // event and as consequence Document::IsShowing() returns false. - return mDocumentNode && mDocumentNode->IsVisible() && - (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded)); - } + bool IsContentLoaded() const; - bool IsHidden() const { return mDocumentNode->Hidden(); } + bool IsHidden() const; /** * Document load states. @@ -239,10 +230,7 @@ * * @return the accessible object */ - Accessible* GetAccessible(nsINode* aNode) const { - return aNode == mDocumentNode ? const_cast(this) - : mNodeToAccessibleMap.Get(aNode); - } + Accessible* GetAccessible(nsINode* aNode) const; /** * Return an accessible for the given node even if the node is not in diff -Nru firefox-84.0.2+build1/accessible/generic/DocAccessible-inl.h firefox-85.0+build1/accessible/generic/DocAccessible-inl.h --- firefox-84.0.2+build1/accessible/generic/DocAccessible-inl.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/DocAccessible-inl.h 2021-01-18 19:58:07.000000000 +0000 @@ -41,6 +41,16 @@ return mVirtualCursor; } +inline bool DocAccessible::IsContentLoaded() const { + // eDOMLoaded flag check is used for error pages as workaround to make this + // method return correct result since error pages do not receive 'pageshow' + // event and as consequence Document::IsShowing() returns false. + return mDocumentNode && mDocumentNode->IsVisible() && + (mDocumentNode->IsShowing() || HasLoadState(eDOMLoaded)); +} + +inline bool DocAccessible::IsHidden() const { return mDocumentNode->Hidden(); } + inline void DocAccessible::FireDelayedEvent(AccEvent* aEvent) { #ifdef A11Y_LOG if (logging::IsEnabled(logging::eDocLoad)) logging::DocLoadEventFired(aEvent); diff -Nru firefox-84.0.2+build1/accessible/generic/HyperTextAccessible.h firefox-85.0+build1/accessible/generic/HyperTextAccessible.h --- firefox-84.0.2+build1/accessible/generic/HyperTextAccessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/generic/HyperTextAccessible.h 2021-01-18 19:58:08.000000000 +0000 @@ -10,6 +10,7 @@ #include "nsIAccessibleText.h" #include "nsIAccessibleTypes.h" #include "nsIFrame.h" // only for nsSelectionAmount +#include "nsISelectionController.h" #include "nsDirection.h" #include "WordMovementType.h" diff -Nru firefox-84.0.2+build1/accessible/interfaces/nsIAccessibleEvent.idl firefox-85.0+build1/accessible/interfaces/nsIAccessibleEvent.idl --- firefox-84.0.2+build1/accessible/interfaces/nsIAccessibleEvent.idl 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/interfaces/nsIAccessibleEvent.idl 2021-01-18 19:58:07.000000000 +0000 @@ -429,9 +429,19 @@ const unsigned long EVENT_ANNOUNCEMENT = 0x0059; /** + * A live region has been introduced. Mac only. + */ + const unsigned long EVENT_LIVE_REGION_ADDED = 0x005A; + + /** + * A live region has been removed (aria-live attribute changed). Mac Only. + */ + const unsigned long EVENT_LIVE_REGION_REMOVED = 0x005B; + + /** * Help make sure event map does not get out-of-line. */ - const unsigned long EVENT_LAST_ENTRY = 0x005A; + const unsigned long EVENT_LAST_ENTRY = 0x005C; /** * The type of event, based on the enumerated event values diff -Nru firefox-84.0.2+build1/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl firefox-85.0+build1/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl --- firefox-84.0.2+build1/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/ipc/extension/mac/PDocAccessiblePlatformExt.ipdl 2021-01-18 19:58:08.000000000 +0000 @@ -6,6 +6,8 @@ include protocol PDocAccessible; +include "mozilla/GfxMessageUtils.h"; + using mozilla::a11y::EWhichRange from "mozilla/a11y/IPCTypes.h"; using nsIntRect from "nsRect.h"; diff -Nru firefox-84.0.2+build1/accessible/ipc/IPCTypes.h firefox-85.0+build1/accessible/ipc/IPCTypes.h --- firefox-84.0.2+build1/accessible/ipc/IPCTypes.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/ipc/IPCTypes.h 2021-01-18 19:58:08.000000000 +0000 @@ -9,6 +9,7 @@ #ifdef ACCESSIBILITY # include "mozilla/a11y/Role.h" +# include "ipc/EnumSerializer.h" namespace IPC { diff -Nru firefox-84.0.2+build1/accessible/ipc/win/PDocAccessible.ipdl firefox-85.0+build1/accessible/ipc/win/PDocAccessible.ipdl --- firefox-84.0.2+build1/accessible/ipc/win/PDocAccessible.ipdl 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/ipc/win/PDocAccessible.ipdl 2021-01-18 19:58:07.000000000 +0000 @@ -7,10 +7,12 @@ include protocol PFileDescriptorSet; include protocol PBrowser; +include "mozilla/GfxMessageUtils.h"; + using mozilla::a11y::role from "mozilla/a11y/IPCTypes.h"; using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h"; using mozilla::a11y::IDispatchHolder from "mozilla/a11y/IPCTypes.h"; -using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h"; +using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h"; using mozilla::LayoutDeviceIntRect from "Units.h"; namespace mozilla { diff -Nru firefox-84.0.2+build1/accessible/mac/AccessibleWrap.mm firefox-85.0+build1/accessible/mac/AccessibleWrap.mm --- firefox-84.0.2+build1/accessible/mac/AccessibleWrap.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/AccessibleWrap.mm 2021-01-18 19:58:08.000000000 +0000 @@ -5,7 +5,7 @@ * 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 "DocAccessible.h" +#include "DocAccessibleWrap.h" #include "nsObjCExceptions.h" #include "nsCocoaUtils.h" @@ -30,7 +30,37 @@ using namespace mozilla::a11y; AccessibleWrap::AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) - : Accessible(aContent, aDoc), mNativeObject(nil), mNativeInited(false) {} + : Accessible(aContent, aDoc), mNativeObject(nil), mNativeInited(false) { + if (aContent && aContent->IsElement() && aDoc) { + // Check if this accessible is a live region and queue it + // it for dispatching an event after it has been inserted. + DocAccessibleWrap* doc = static_cast(aDoc); + static const dom::Element::AttrValuesArray sLiveRegionValues[] = { + nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr}; + int32_t attrValue = aContent->AsElement()->FindAttrValueIn( + kNameSpaceID_None, nsGkAtoms::aria_live, sLiveRegionValues, + eIgnoreCase); + if (attrValue == 0) { + // aria-live is "off", do nothing. + } else if (attrValue > 0) { + // aria-live attribute is polite or assertive. It's live! + doc->QueueNewLiveRegion(this); + } else if (const nsRoleMapEntry* roleMap = + aria::GetRoleMap(aContent->AsElement())) { + // aria role defines it as a live region. It's live! + if (roleMap->liveAttRule == ePoliteLiveAttr || + roleMap->liveAttRule == eAssertiveLiveAttr) { + doc->QueueNewLiveRegion(this); + } + } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute( + aContent, nsGkAtoms::live)) { + // HTML element defines it as a live region. It's live! + if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) { + doc->QueueNewLiveRegion(this); + } + } + } +} AccessibleWrap::~AccessibleWrap() {} @@ -120,11 +150,17 @@ nsresult rv = Accessible::HandleAccEvent(aEvent); NS_ENSURE_SUCCESS(rv, rv); + uint32_t eventType = aEvent->GetEventType(); + + if (eventType == nsIAccessibleEvent::EVENT_SHOW) { + DocAccessibleWrap* doc = static_cast(Document()); + doc->ProcessNewLiveRegions(); + } + if (IPCAccessibilityActive()) { return NS_OK; } - uint32_t eventType = aEvent->GetEventType(); Accessible* eventTarget = nullptr; switch (eventType) { @@ -222,6 +258,9 @@ case nsIAccessibleEvent::EVENT_SELECTION: case nsIAccessibleEvent::EVENT_SELECTION_ADD: case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: + case nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED: + case nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED: + case nsIAccessibleEvent::EVENT_NAME_CHANGE: [nativeAcc handleAccessibleEvent:eventType]; break; @@ -294,6 +333,10 @@ return [mozOptionAccessible class]; } + case roles::RICH_OPTION: { + return [mozSelectableChildAccessible class]; + } + case roles::COMBOBOX_LIST: case roles::MENUBAR: case roles::MENUPOPUP: { diff -Nru firefox-84.0.2+build1/accessible/mac/DocAccessibleWrap.h firefox-85.0+build1/accessible/mac/DocAccessibleWrap.h --- firefox-84.0.2+build1/accessible/mac/DocAccessibleWrap.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/DocAccessibleWrap.h 2021-01-18 19:58:08.000000000 +0000 @@ -20,9 +20,23 @@ public: DocAccessibleWrap(dom::Document* aDocument, PresShell* aPresShell); + virtual ~DocAccessibleWrap(); + virtual void Shutdown() override; - virtual ~DocAccessibleWrap(); + virtual void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID, + nsAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue) override; + + void QueueNewLiveRegion(Accessible* aAccessible); + + void ProcessNewLiveRegions(); + + protected: + virtual void DoInitialUpdate() override; + + private: + nsTHashtable mNewLiveRegions; }; } // namespace a11y diff -Nru firefox-84.0.2+build1/accessible/mac/DocAccessibleWrap.mm firefox-85.0+build1/accessible/mac/DocAccessibleWrap.mm --- firefox-84.0.2+build1/accessible/mac/DocAccessibleWrap.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/DocAccessibleWrap.mm 2021-01-18 19:58:08.000000000 +0000 @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DocAccessibleWrap.h" +#include "DocAccessible-inl.h" #import "mozAccessible.h" #import "MOXTextMarkerDelegate.h" @@ -23,3 +24,81 @@ } DocAccessibleWrap::~DocAccessibleWrap() {} + +void DocAccessibleWrap::AttributeChanged(dom::Element* aElement, + int32_t aNameSpaceID, + nsAtom* aAttribute, int32_t aModType, + const nsAttrValue* aOldValue) { + DocAccessible::AttributeChanged(aElement, aNameSpaceID, aAttribute, aModType, + aOldValue); + if (aAttribute == nsGkAtoms::aria_live) { + Accessible* accessible = + mContent != aElement ? GetAccessible(aElement) : this; + if (!accessible) { + return; + } + + static const dom::Element::AttrValuesArray sLiveRegionValues[] = { + nsGkAtoms::OFF, nsGkAtoms::polite, nsGkAtoms::assertive, nullptr}; + int32_t attrValue = + aElement->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::aria_live, + sLiveRegionValues, eIgnoreCase); + if (attrValue > 0) { + if (!aOldValue || aOldValue->IsEmptyString() || + aOldValue->Equals(nsGkAtoms::OFF, eIgnoreCase)) { + // This element just got an active aria-live attribute value + FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED, + accessible); + } + } else { + if (aOldValue && (aOldValue->Equals(nsGkAtoms::polite, eIgnoreCase) || + aOldValue->Equals(nsGkAtoms::assertive, eIgnoreCase))) { + // This element lost an active live region + FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, + accessible); + } else if (attrValue == 0) { + // aria-live="off", check if its a role-based live region that + // needs to be removed. + if (const nsRoleMapEntry* roleMap = accessible->ARIARoleMap()) { + // aria role defines it as a live region. It's live! + if (roleMap->liveAttRule == ePoliteLiveAttr || + roleMap->liveAttRule == eAssertiveLiveAttr) { + FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, + accessible); + } + } else if (nsStaticAtom* value = GetAccService()->MarkupAttribute( + aElement, nsGkAtoms::live)) { + // HTML element defines it as a live region. It's live! + if (value == nsGkAtoms::polite || value == nsGkAtoms::assertive) { + FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED, + accessible); + } + } + } + } + } +} + +void DocAccessibleWrap::QueueNewLiveRegion(Accessible* aAccessible) { + if (!aAccessible) { + return; + } + + mNewLiveRegions.PutEntry(aAccessible->UniqueID()); +} + +void DocAccessibleWrap::ProcessNewLiveRegions() { + for (auto iter = mNewLiveRegions.Iter(); !iter.Done(); iter.Next()) { + if (Accessible* liveRegion = + GetAccessibleByUniqueID(const_cast(iter.Get()->GetKey()))) { + FireDelayedEvent(nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED, liveRegion); + } + } + + mNewLiveRegions.Clear(); +} + +void DocAccessibleWrap::DoInitialUpdate() { + DocAccessible::DoInitialUpdate(); + ProcessNewLiveRegions(); +} diff -Nru firefox-84.0.2+build1/accessible/mac/GeckoTextMarker.h firefox-85.0+build1/accessible/mac/GeckoTextMarker.h --- firefox-84.0.2+build1/accessible/mac/GeckoTextMarker.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/GeckoTextMarker.h 2021-01-18 19:58:08.000000000 +0000 @@ -59,8 +59,6 @@ } private: - uint32_t CharacterCount(const AccessibleOrProxy& aContainer); - bool IsEditableRoot(); }; @@ -70,6 +68,8 @@ const GeckoTextMarker& aEnd) : mStart(aStart), mEnd(aEnd) {} + GeckoTextMarkerRange() {} + GeckoTextMarkerRange(AccessibleOrProxy aDoc, AXTextMarkerRangeRef aTextMarkerRange); @@ -101,6 +101,13 @@ */ MOZ_CAN_RUN_SCRIPT_BOUNDARY void Select() const; + /** + * Crops the range if it overlaps the given accessible element boundaries. + * Return true if successfully cropped. false if the range does not intersect + * with the container. + */ + bool Crop(const AccessibleOrProxy& aContainer); + GeckoTextMarker mStart; GeckoTextMarker mEnd; }; diff -Nru firefox-84.0.2+build1/accessible/mac/GeckoTextMarker.mm firefox-85.0+build1/accessible/mac/GeckoTextMarker.mm --- firefox-84.0.2+build1/accessible/mac/GeckoTextMarker.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/GeckoTextMarker.mm 2021-01-18 19:58:08.000000000 +0000 @@ -277,7 +277,7 @@ return false; } -uint32_t GeckoTextMarker::CharacterCount(const AccessibleOrProxy& aContainer) { +static uint32_t CharacterCount(const AccessibleOrProxy& aContainer) { if (aContainer.IsProxy()) { return aContainer.AsProxy()->CharacterCount(); } @@ -335,7 +335,8 @@ GeckoTextMarkerRange::GeckoTextMarkerRange( AccessibleOrProxy aDoc, AXTextMarkerRangeRef aTextMarkerRange) { - if (CFGetTypeID(aTextMarkerRange) != AXTextMarkerRangeGetTypeID()) { + if (!aTextMarkerRange || + CFGetTypeID(aTextMarkerRange) != AXTextMarkerRangeGetTypeID()) { return; } @@ -352,16 +353,26 @@ GeckoTextMarkerRange::GeckoTextMarkerRange( const AccessibleOrProxy& aAccessible) { - mStart = GeckoTextMarker(aAccessible.Parent(), 0); - mEnd = GeckoTextMarker(aAccessible.Parent(), 0); - if (mStart.mContainer.IsProxy()) { - DocAccessibleParent* ipcDoc = mStart.mContainer.AsProxy()->Document(); - Unused << ipcDoc->GetPlatformExtension()->SendRangeOfChild( - mStart.mContainer.AsProxy()->ID(), aAccessible.AsProxy()->ID(), - &mStart.mOffset, &mEnd.mOffset); - } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) { - htWrap->RangeOfChild(aAccessible.AsAccessible(), &mStart.mOffset, - &mEnd.mOffset); + if ((aAccessible.IsAccessible() && + aAccessible.AsAccessible()->IsHyperText()) || + (aAccessible.IsProxy() && aAccessible.AsProxy()->mIsHyperText)) { + // The accessible is a hypertext. Initialize range to its inner text range. + mStart = GeckoTextMarker(aAccessible, 0); + mEnd = GeckoTextMarker(aAccessible, (CharacterCount(aAccessible))); + } else { + // The accessible is not a hypertext (maybe a text leaf?). Initialize range + // to its offsets in its container. + mStart = GeckoTextMarker(aAccessible.Parent(), 0); + mEnd = GeckoTextMarker(aAccessible.Parent(), 0); + if (mStart.mContainer.IsProxy()) { + DocAccessibleParent* ipcDoc = mStart.mContainer.AsProxy()->Document(); + Unused << ipcDoc->GetPlatformExtension()->SendRangeOfChild( + mStart.mContainer.AsProxy()->ID(), aAccessible.AsProxy()->ID(), + &mStart.mOffset, &mEnd.mOffset); + } else if (auto htWrap = mStart.ContainerAsHyperTextWrap()) { + htWrap->RangeOfChild(aAccessible.AsAccessible(), &mStart.mOffset, + &mEnd.mOffset); + } } } @@ -441,5 +452,29 @@ htWrap->SelectRange(mStart.mOffset, end, mEnd.mOffset); } } + +bool GeckoTextMarkerRange::Crop(const AccessibleOrProxy& aContainer) { + GeckoTextMarker containerStart(aContainer, 0); + GeckoTextMarker containerEnd(aContainer, CharacterCount(aContainer)); + + if (mEnd < containerStart || containerEnd < mStart) { + // The range ends before the container, or starts after it. + return false; + } + + if (mStart < containerStart) { + // If range start is before container start, adjust range start to + // start of container. + mStart = containerStart; + } + + if (containerEnd < mEnd) { + // If range end is after container end, adjust range end to end of + // container. + mEnd = containerEnd; + } + + return true; +} } } diff -Nru firefox-84.0.2+build1/accessible/mac/HyperTextAccessibleWrap.mm firefox-85.0+build1/accessible/mac/HyperTextAccessibleWrap.mm --- firefox-84.0.2+build1/accessible/mac/HyperTextAccessibleWrap.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/HyperTextAccessibleWrap.mm 2021-01-18 19:58:07.000000000 +0000 @@ -22,14 +22,12 @@ class HyperTextIterator { public: HyperTextIterator(HyperTextAccessible* aStartContainer, int32_t aStartOffset, - HyperTextAccessible* aEndContainer, int32_t aEndOffset, - bool aSkipBullet = false) + HyperTextAccessible* aEndContainer, int32_t aEndOffset) : mCurrentContainer(aStartContainer), mCurrentStartOffset(aStartOffset), mCurrentEndOffset(aStartOffset), mEndContainer(aEndContainer), - mEndOffset(aEndOffset), - mSkipBullet(aSkipBullet) {} + mEndOffset(aEndOffset) {} bool Next(); @@ -56,8 +54,6 @@ HyperTextAccessible* mEndContainer; int32_t mEndOffset; - - bool mSkipBullet; }; bool HyperTextIterator::NormalizeForward() { @@ -83,6 +79,12 @@ mCurrentContainer = mCurrentContainer->Parent()->AsHyperText(); mCurrentStartOffset = endOffset; + if (mCurrentContainer == mEndContainer && + mCurrentStartOffset >= mEndOffset) { + // Reached end boundary. + return false; + } + // Call NormalizeForward recursively to get top-most link if at the end of // one, or innermost link if at the beginning. NormalizeForward(); @@ -94,14 +96,27 @@ // If there is a link at this offset, mutate into it. if (link && link->IsHyperText()) { + if (mCurrentStartOffset > 0 && + mCurrentContainer->LinkIndexAtOffset(mCurrentStartOffset) == + mCurrentContainer->LinkIndexAtOffset(mCurrentStartOffset - 1)) { + MOZ_ASSERT_UNREACHABLE("Same link for previous offset"); + return false; + } + mCurrentContainer = link->AsHyperText(); - if (mSkipBullet && link->IsHTMLListItem()) { + if (link->IsHTMLListItem()) { Accessible* bullet = link->AsHTMLListItem()->Bullet(); mCurrentStartOffset = bullet ? nsAccUtils::TextLength(bullet) : 0; } else { mCurrentStartOffset = 0; } + if (mCurrentContainer == mEndContainer && + mCurrentStartOffset >= mEndOffset) { + // Reached end boundary. + return false; + } + // Call NormalizeForward recursively to get top-most embedding ancestor // if at the end of one, or innermost link if at the beginning. NormalizeForward(); @@ -153,7 +168,7 @@ return true; } - if (mSkipBullet && mCurrentContainer->IsHTMLListItem() && + if (mCurrentContainer->IsHTMLListItem() && mCurrentContainer->AsHTMLListItem()->Bullet() == link) { mCurrentStartOffset = 0; NormalizeBackward(); @@ -219,6 +234,16 @@ int32_t aStartOffset, HyperTextAccessible* aEndContainer, int32_t aEndOffset) { + if (IsHTMLListItem()) { + Accessible* maybeBullet = GetChildAtOffset(aStartOffset - 1); + if (maybeBullet) { + Accessible* bullet = AsHTMLListItem()->Bullet(); + if (maybeBullet == bullet) { + TextSubstring(0, nsAccUtils::TextLength(bullet), aText); + } + } + } + HyperTextIterator iter(this, aStartOffset, aEndContainer, aEndOffset); while (iter.Next()) { nsAutoString text; @@ -246,7 +271,7 @@ int32_t aStartOffset, HyperTextAccessible* aEndContainer, int32_t aEndOffset) { int32_t length = 0; - HyperTextIterator iter(this, aStartOffset, aEndContainer, aEndOffset, true); + HyperTextIterator iter(this, aStartOffset, aEndContainer, aEndOffset); while (iter.Next()) { length += iter.SegmentLength(); } @@ -258,7 +283,7 @@ HyperTextAccessible** aContainer, int32_t* aOffset) { int32_t index = aIndex; - HyperTextIterator iter(this, 0, this, CharacterCount(), true); + HyperTextIterator iter(this, 0, this, CharacterCount()); while (iter.Next()) { int32_t segmentLength = iter.SegmentLength(); if (index <= segmentLength) { @@ -518,7 +543,9 @@ Accessible* HyperTextAccessibleWrap::LeafAtOffset(int32_t aOffset) { HyperTextAccessible* text = this; Accessible* child = nullptr; - int32_t innerOffset = aOffset; + // The offset needed should "attach" the previous accessible if + // in between two accessibles. + int32_t innerOffset = aOffset > 0 ? aOffset - 1 : aOffset; do { int32_t childIdx = text->GetChildIndexAtOffset(innerOffset); if (childIdx == -1) { @@ -550,7 +577,7 @@ EWordMovementType aWordMovementType) { // Layout can remain trapped in an editable. We normalize out of // it if we are in its last offset. - HyperTextIterator iter(this, aOffset, this, CharacterCount(), true); + HyperTextIterator iter(this, aOffset, this, CharacterCount()); if (aDirection == eDirNext) { iter.NormalizeForward(); } else { diff -Nru firefox-84.0.2+build1/accessible/mac/MOXAccessibleBase.h firefox-85.0+build1/accessible/mac/MOXAccessibleBase.h --- firefox-84.0.2+build1/accessible/mac/MOXAccessibleBase.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXAccessibleBase.h 2021-01-18 19:58:08.000000000 +0000 @@ -123,6 +123,8 @@ // override - (id)moxTextMarkerDelegate; +- (BOOL)moxIsLiveRegion; + #pragma mark - - (NSString*)description; diff -Nru firefox-84.0.2+build1/accessible/mac/MOXAccessibleBase.mm firefox-85.0+build1/accessible/mac/MOXAccessibleBase.mm --- firefox-84.0.2+build1/accessible/mac/MOXAccessibleBase.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXAccessibleBase.mm 2021-01-18 19:58:08.000000000 +0000 @@ -511,6 +511,10 @@ return nil; } +- (BOOL)moxIsLiveRegion { + return NO; +} + #pragma mark - // objc-style description (from NSObject); not to be confused with the diff -Nru firefox-84.0.2+build1/accessible/mac/MOXAccessibleProtocol.h firefox-85.0+build1/accessible/mac/MOXAccessibleProtocol.h --- firefox-84.0.2+build1/accessible/mac/MOXAccessibleProtocol.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXAccessibleProtocol.h 2021-01-18 19:58:08.000000000 +0000 @@ -57,6 +57,9 @@ // Return text delegate if it exists. - (id _Nullable)moxTextMarkerDelegate; +// Return true if this accessible is a live region +- (BOOL)moxIsLiveRegion; + @optional #pragma mark - AttributeGetters @@ -281,6 +284,21 @@ // AXEditableAncestor - (id _Nullable)moxEditableAncestor; +// AXHighestEditableAncestor +- (id _Nullable)moxHighestEditableAncestor; + +// AXFocusableAncestor +- (id _Nullable)moxFocusableAncestor; + +// AXARIAAtomic +- (NSNumber* _Nullable)moxARIAAtomic; + +// AXARIALive +- (NSString* _Nullable)moxARIALive; + +// AXARIARelevant +- (NSString* _Nullable)moxARIARelevant; + // AXMozDebugDescription - (NSString* _Nullable)moxMozDebugDescription; diff -Nru firefox-84.0.2+build1/accessible/mac/MOXSearchInfo.mm firefox-85.0+build1/accessible/mac/MOXSearchInfo.mm --- firefox-84.0.2+build1/accessible/mac/MOXSearchInfo.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXSearchInfo.mm 2021-01-18 19:58:07.000000000 +0000 @@ -120,32 +120,24 @@ RotorRule rule = mImmediateDescendantsOnly ? RotorRule(geckoRootAcc) : RotorRule(); - if (mSearchForward) { - if ([mStartElem isKindOfClass:[MOXWebAreaAccessible class]]) { - if (id rootGroup = - [static_cast(mStartElem) rootGroup]) { - // Moving forward from web area, rootgroup; if it exists, is next. - [matches addObject:rootGroup]; - if (mResultLimit == 1) { - // Found one match, continue in search keys for block. - continue; - } + if ([mStartElem isKindOfClass:[MOXWebAreaAccessible class]]) { + if (id rootGroup = + [static_cast(mStartElem) rootGroup]) { + // Moving forward from web area, rootgroup; if it exists, is next. + [matches addObject:rootGroup]; + if (mResultLimit == 1) { + // Found one match, continue in search keys for block. + continue; } - } else if (mImmediateDescendantsOnly && mStartElem != mRoot && - [mStartElem isKindOfClass:[MOXRootGroup class]]) { - // Moving forward from root group. If we don't match descendants, - // there is no match. Continue. - continue; - } - } else if (!mSearchForward && - [mStartElem isKindOfClass:[MOXRootGroup class]]) { - // Moving backward from root group. Web area is next. - [matches addObject:[mStartElem moxParent]]; - if (mResultLimit == 1) { - // Found one match, continue in search keys for block. - continue; } } + + if (mImmediateDescendantsOnly && mStartElem != mRoot && + [mStartElem isKindOfClass:[MOXRootGroup class]]) { + // Moving forward from root group. If we don't match descendants, + // there is no match. Continue. + continue; + } [matches addObjectsFromArray:[self getMatchesForRule:rule]]; } @@ -323,10 +315,16 @@ } if ([key isEqualToString:@"AXTextFieldSearchKey"]) { - RotorMacRoleRule rule = - mImmediateDescendantsOnly - ? RotorMacRoleRule(@"AXTextField", geckoRootAcc) - : RotorMacRoleRule(@"AXTextField"); + RotorTextEntryRule rule = mImmediateDescendantsOnly + ? RotorTextEntryRule(geckoRootAcc) + : RotorTextEntryRule(); + [matches addObjectsFromArray:[self getMatchesForRule:rule]]; + } + + if ([key isEqualToString:@"AXLiveRegionSearchKey"]) { + RotorLiveRegionRule rule = mImmediateDescendantsOnly + ? RotorLiveRegionRule(geckoRootAcc) + : RotorLiveRegionRule(); [matches addObjectsFromArray:[self getMatchesForRule:rule]]; } } diff -Nru firefox-84.0.2+build1/accessible/mac/MOXTextMarkerDelegate.h firefox-85.0+build1/accessible/mac/MOXTextMarkerDelegate.h --- firefox-84.0.2+build1/accessible/mac/MOXTextMarkerDelegate.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXTextMarkerDelegate.h 2021-01-18 19:58:07.000000000 +0000 @@ -8,6 +8,7 @@ #import #import "MOXAccessibleProtocol.h" +#import "GeckoTextMarker.h" #include "AccessibleOrProxy.h" @@ -31,6 +32,8 @@ - (void)invalidateSelection; +- (mozilla::a11y::GeckoTextMarkerRange)selection; + // override - (id)moxStartTextMarker; diff -Nru firefox-84.0.2+build1/accessible/mac/MOXTextMarkerDelegate.mm firefox-85.0+build1/accessible/mac/MOXTextMarkerDelegate.mm --- firefox-84.0.2+build1/accessible/mac/MOXTextMarkerDelegate.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/MOXTextMarkerDelegate.mm 2021-01-18 19:58:07.000000000 +0000 @@ -75,6 +75,10 @@ mSelection = nil; } +- (mozilla::a11y::GeckoTextMarkerRange)selection { + return mozilla::a11y::GeckoTextMarkerRange(mGeckoDocAccessible, mSelection); +} + - (id)moxStartTextMarker { GeckoTextMarker geckoTextPoint(mGeckoDocAccessible, 0); return geckoTextPoint.CreateAXTextMarker(); @@ -287,7 +291,12 @@ return nil; } - return GetNativeFromGeckoAccessible(geckoTextMarker.Leaf()); + AccessibleOrProxy leaf = geckoTextMarker.Leaf(); + if (leaf.IsNull()) { + return nil; + } + + return GetNativeFromGeckoAccessible(leaf); } - (id)moxTextMarkerRangeForUIElement:(id)element { diff -Nru firefox-84.0.2+build1/accessible/mac/mozAccessible.h firefox-85.0+build1/accessible/mac/mozAccessible.h --- firefox-84.0.2+build1/accessible/mac/mozAccessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozAccessible.h 2021-01-18 19:58:08.000000000 +0000 @@ -59,6 +59,8 @@ uint64_t mCachedState; nsStaticAtom* mARIARole; + + bool mIsLiveRegion; } // inits with the given wrap or proxy accessible @@ -109,6 +111,9 @@ // Handle a role change - (void)handleRoleChanged:(mozilla::a11y::role)newRole; +// Get ARIA role +- (nsStaticAtom*)ARIARole; + #pragma mark - mozAccessible protocol / widget // override @@ -133,6 +138,8 @@ - (id)moxTextMarkerDelegate; +- (BOOL)moxIsLiveRegion; + // Attribute getters // override @@ -184,6 +191,15 @@ - (NSString*)moxARIACurrent; // override +- (NSNumber*)moxARIAAtomic; + +// override +- (NSString*)moxARIALive; + +// override +- (NSString*)moxARIARelevant; + +// override - (id)moxTitleUIElement; // override @@ -195,6 +211,12 @@ // override - (id)moxEditableAncestor; +// override +- (id)moxHighestEditableAncestor; + +// override +- (id)moxFocusableAncestor; + #ifndef RELEASE_OR_BETA // override - (NSString*)moxMozDebugDescription; diff -Nru firefox-84.0.2+build1/accessible/mac/mozAccessible.mm firefox-85.0+build1/accessible/mac/mozAccessible.mm --- firefox-84.0.2+build1/accessible/mac/mozAccessible.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozAccessible.mm 2021-01-18 19:58:08.000000000 +0000 @@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #import "mozAccessible.h" +#include "MOXAccessibleBase.h" #import "MacUtils.h" #import "mozView.h" @@ -40,7 +41,7 @@ @interface mozAccessible () - (BOOL)providesLabelNotTitle; -- (nsStaticAtom*)ARIARole; +- (void)maybePostLiveRegionChanged; @end @implementation mozAccessible @@ -207,6 +208,12 @@ return [self stateWithMask:states::FOCUSABLE] == 0; } + if (selector == @selector(moxARIALive) || + selector == @selector(moxARIAAtomic) || + selector == @selector(moxARIARelevant)) { + return ![self moxIsLiveRegion]; + } + return [super moxBlockSelector:selector]; } @@ -260,6 +267,10 @@ getOrCreateForDoc:mGeckoAccessible.AsProxy()->Document()]; } +- (BOOL)moxIsLiveRegion { + return mIsLiveRegion; +} + - (id)moxHitTest:(NSPoint)point { MOZ_ASSERT(!mGeckoAccessible.IsNull()); @@ -538,6 +549,13 @@ }; - (NSString*)moxRoleDescription { + if (NSString* ariaRoleDescription = + utils::GetAccAttr(self, "roledescription")) { + if ([ariaRoleDescription length]) { + return ariaRoleDescription; + } + } + if (mRole == roles::FIGURE) return utils::LocalizedString(u"figure"_ns); if (mRole == roles::HEADING) return utils::LocalizedString(u"heading"_ns); @@ -709,6 +727,23 @@ return utils::GetAccAttr(self, "current"); } +- (NSNumber*)moxARIAAtomic { + return @(utils::GetAccAttr(self, "atomic") != nil); +} + +- (NSString*)moxARIALive { + return utils::GetAccAttr(self, "live"); +} + +- (NSString*)moxARIARelevant { + if (NSString* relevant = utils::GetAccAttr(self, "container-relevant")) { + return relevant; + } + + // Default aria-relevant value + return @"additions text"; +} + - (id)moxTitleUIElement { MOZ_ASSERT(!mGeckoAccessible.IsNull()); @@ -795,6 +830,33 @@ return nil; } +- (id)moxHighestEditableAncestor { + id highestAncestor = [self moxEditableAncestor]; + while ([highestAncestor conformsToProtocol:@protocol(MOXAccessible)]) { + id ancestorParent = [highestAncestor moxUnignoredParent]; + if (![ancestorParent conformsToProtocol:@protocol(MOXAccessible)]) { + break; + } + + id higherAncestor = [ancestorParent moxEditableAncestor]; + + if (!higherAncestor) { + break; + } + + highestAncestor = higherAncestor; + } + + return highestAncestor; +} + +- (id)moxFocusableAncestor { + // XXX: Checking focusable state up the chain can be expensive. For now, + // we can just return AXEditableAncestor since the main use case for this + // is rich text editing with links. + return [self moxEditableAncestor]; +} + #ifndef RELEASE_OR_BETA - (NSString*)moxMozDebugDescription { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; @@ -911,11 +973,20 @@ return NO; } +- (void)maybePostLiveRegionChanged { + for (id element = self; [element conformsToProtocol:@protocol(MOXAccessible)]; + element = [element moxUnignoredParent]) { + if ([element moxIsLiveRegion]) { + [element moxPostNotification:@"AXLiveRegionChanged"]; + return; + } + } +} + - (void)handleAccessibleTextChangeEvent:(NSString*)change inserted:(BOOL)isInserted inContainer:(const AccessibleOrProxy&)container at:(int32_t)start { - // XXX: Eventually live region handling will go here. } - (void)handleAccessibleEvent:(uint32_t)eventType { @@ -963,6 +1034,17 @@ withUserInfo:userInfo]; break; } + case nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED: + mIsLiveRegion = true; + [self moxPostNotification:@"AXLiveRegionCreated"]; + break; + case nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED: + mIsLiveRegion = false; + break; + case nsIAccessibleEvent::EVENT_REORDER: + case nsIAccessibleEvent::EVENT_NAME_CHANGE: + [self maybePostLiveRegionChanged]; + break; } } diff -Nru firefox-84.0.2+build1/accessible/mac/mozActionElements.h firefox-85.0+build1/accessible/mac/mozActionElements.h --- firefox-84.0.2+build1/accessible/mac/mozActionElements.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozActionElements.h 2021-01-18 19:58:08.000000000 +0000 @@ -34,9 +34,6 @@ // override - (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled; -// override -- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent; - @end @interface mozCheckboxAccessible : mozButtonAccessible @@ -44,6 +41,9 @@ // override - (id)moxValue; +// override +- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled; + @end // Accessible for a radio button diff -Nru firefox-84.0.2+build1/accessible/mac/mozActionElements.mm firefox-85.0+build1/accessible/mac/mozActionElements.mm --- firefox-84.0.2+build1/accessible/mac/mozActionElements.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozActionElements.mm 2021-01-18 19:58:08.000000000 +0000 @@ -77,23 +77,6 @@ } } -- (BOOL)moxIgnoreWithParent:(mozAccessible*)parent { - if (Accessible* acc = mGeckoAccessible.AsAccessible()) { - if (acc->IsContent() && - acc->GetContent()->IsXULElement(nsGkAtoms::menulist)) { - // The way select elements work is that content has a COMBOBOX accessible, - // when it is clicked it expands a COMBOBOX in our top-level main XUL - // window. The latter COMBOBOX is a stand-in for the content one while it - // is expanded. - // XXX: VO focus behaves weirdly if we expose the dummy XUL COMBOBOX in - // the tree. We are only interested in its menu child. - return YES; - } - } - - return [super moxIgnoreWithParent:parent]; -} - @end @implementation mozRadioButtonAccessible @@ -154,6 +137,14 @@ NS_OBJC_END_TRY_ABORT_BLOCK_NIL; } +- (void)stateChanged:(uint64_t)state isEnabled:(BOOL)enabled { + [super stateChanged:state isEnabled:enabled]; + + if (state & (states::CHECKED | states::PRESSED | states::MIXED)) { + [self moxPostNotification:NSAccessibilityValueChangedNotification]; + } +} + @end @implementation mozPaneAccessible diff -Nru firefox-84.0.2+build1/accessible/mac/mozTableAccessible.h firefox-85.0+build1/accessible/mac/mozTableAccessible.h --- firefox-84.0.2+build1/accessible/mac/mozTableAccessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozTableAccessible.h 2021-01-18 19:58:08.000000000 +0000 @@ -125,6 +125,9 @@ // override - (NSArray*)moxSelectedRows; +// override +- (NSString*)moxOrientation; + @end @interface mozOutlineRowAccessible : mozTableRowAccessible @@ -136,6 +139,9 @@ - (NSNumber*)moxDisclosing; // override +- (NSNumber*)moxExpanded; + +// override - (id)moxDisclosedByRow; // override diff -Nru firefox-84.0.2+build1/accessible/mac/mozTableAccessible.mm firefox-85.0+build1/accessible/mac/mozTableAccessible.mm --- firefox-84.0.2+build1/accessible/mac/mozTableAccessible.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozTableAccessible.mm 2021-01-18 19:58:08.000000000 +0000 @@ -14,6 +14,7 @@ #include "Accessible.h" #include "TableAccessible.h" #include "TableCellAccessible.h" +#include "XULTreeAccessible.h" #include "Pivot.h" #include "Relation.h" @@ -409,7 +410,26 @@ } - (NSArray*)moxColumns { - // Webkit says we shouldn't do anything here + if (Accessible* acc = mGeckoAccessible.AsAccessible()) { + if (acc->IsContent() && acc->GetContent()->IsXULElement(nsGkAtoms::tree)) { + XULTreeAccessible* treeAcc = (XULTreeAccessible*)acc; + NSMutableArray* cols = [[NSMutableArray alloc] init]; + // XUL trees store their columns in a group at the tree's first + // child. Here, we iterate over that group to get each column's + // native accessible and add it to our col array. + Accessible* treeColumns = treeAcc->GetChildAt(0); + if (treeColumns) { + uint32_t colCount = treeColumns->ChildCount(); + for (uint32_t i = 0; i < colCount; i++) { + Accessible* treeColumnItem = treeColumns->GetChildAt(i); + [cols addObject:GetNativeFromGeckoAccessible(treeColumnItem)]; + } + return cols; + } + } + } + // Webkit says we shouldn't expose any cols for aria-tree + // so we return an empty array here return @[]; } @@ -425,6 +445,10 @@ return selectedRows; } +- (NSString*)moxOrientation { + return NSAccessibilityVerticalOrientationValue; +} + @end @implementation mozOutlineRowAccessible @@ -437,6 +461,10 @@ return @([self stateWithMask:states::EXPANDED] != 0); } +- (NSNumber*)moxExpanded { + return @([self stateWithMask:states::EXPANDED] != 0); +} + - (id)moxDisclosedByRow { // According to webkit: this attr corresponds to the row // that contains this row. It should be the same as the @@ -492,8 +520,9 @@ } else if (ProxyAccessible* proxy = mGeckoAccessible.AsProxy()) { groupPos = proxy->GroupPosition(); } - - return @(groupPos.level); + // mac expects 0-indexed levels, but groupPos.level is 1-indexed + // so we subtract 1 here for levels above 0 + return groupPos.level > 0 ? @(groupPos.level - 1) : @(groupPos.level); } - (NSArray*)moxDisclosedRows { diff -Nru firefox-84.0.2+build1/accessible/mac/mozTextAccessible.h firefox-85.0+build1/accessible/mac/mozTextAccessible.h --- firefox-84.0.2+build1/accessible/mac/mozTextAccessible.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozTextAccessible.h 2021-01-18 19:58:08.000000000 +0000 @@ -25,6 +25,9 @@ - (NSNumber*)moxInsertionPointLineNumber; // override +- (NSString*)moxRole; + +// override - (NSString*)moxSubrole; // override diff -Nru firefox-84.0.2+build1/accessible/mac/mozTextAccessible.mm firefox-85.0+build1/accessible/mac/mozTextAccessible.mm --- firefox-84.0.2+build1/accessible/mac/mozTextAccessible.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/mozTextAccessible.mm 2021-01-18 19:58:08.000000000 +0000 @@ -15,6 +15,7 @@ #import "mozTextAccessible.h" #import "GeckoTextMarker.h" +#import "MOXTextMarkerDelegate.h" using namespace mozilla; using namespace mozilla::a11y; @@ -43,6 +44,8 @@ - (long)textLength; - (BOOL)isReadOnly; - (NSString*)text; +- (GeckoTextMarkerRange)selection; +- (GeckoTextMarkerRange)textMarkerRangeFromRange:(NSValue*)range; @end @implementation mozTextAccessible @@ -131,6 +134,15 @@ return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil; } +- (NSString*)moxRole { + if ([self ARIARole] == nsGkAtoms::textbox || + [self stateWithMask:states::MULTI_LINE]) { + return NSAccessibilityTextAreaRole; + } + + return [super moxRole]; +} + - (NSString*)moxSubrole { MOZ_ASSERT(!mGeckoAccessible.IsNull()); @@ -150,80 +162,35 @@ } - (NSNumber*)moxNumberOfCharacters { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - - Accessible* acc = mGeckoAccessible.AsAccessible(); - ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); - HyperTextAccessible* textAcc = acc ? acc->AsHyperText() : nullptr; - if (!textAcc && !proxy) { - return @0; - } - - return textAcc ? @(textAcc->CharacterCount()) : @(proxy->CharacterCount()); + return @([self textLength]); } - (NSString*)moxSelectedText { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - - int32_t start = 0, end = 0; - nsAutoString selText; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - textAcc->SelectionBoundsAt(0, &start, &end); - if (start != end) { - textAcc->TextSubstring(start, end, selText); - } - } - } else { - mGeckoAccessible.AsProxy()->SelectionBoundsAt(0, selText, &start, &end); + GeckoTextMarkerRange selection = [self selection]; + if (!selection.IsValid()) { + return nil; } - return nsCocoaUtils::ToNSString(selText); + return selection.Text(); } - (NSValue*)moxSelectedTextRange { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - - int32_t start = 0; - int32_t end = 0; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - if (textAcc->SelectionCount()) { - textAcc->SelectionBoundsAt(0, &start, &end); - } else { - start = textAcc->CaretOffset(); - end = start != -1 ? start : 0; - } - } - } else { - ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); - if (proxy->SelectionCount()) { - nsString data; - proxy->SelectionBoundsAt(0, data, &start, &end); - } else { - start = proxy->CaretOffset(); - end = start != -1 ? start : 0; - } + GeckoTextMarkerRange selection = [self selection]; + if (!selection.IsValid()) { + return nil; } - return [NSValue valueWithRange:NSMakeRange(start, end - start)]; + GeckoTextMarkerRange fromStartToSelection( + GeckoTextMarker(mGeckoAccessible, 0), selection.mStart); + + return [NSValue valueWithRange:NSMakeRange(fromStartToSelection.Length(), + selection.Length())]; } - (NSValue*)moxVisibleCharacterRange { // XXX this won't work with Textarea and such as we actually don't give // the visible character range. - Accessible* acc = mGeckoAccessible.AsAccessible(); - ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); - HyperTextAccessible* textAcc = acc ? acc->AsHyperText() : nullptr; - if (!textAcc && !proxy) { - return nil; - } - - return [NSValue - valueWithRange:NSMakeRange(0, textAcc ? textAcc->CharacterCount() - : proxy->CharacterCount())]; + return [NSValue valueWithRange:NSMakeRange(0, [self textLength])]; } - (BOOL)moxBlockSelector:(SEL)selector { @@ -278,23 +245,10 @@ } - (void)moxSetSelectedTextRange:(NSValue*)selectedTextRange { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - - NSRange range; - if (!ToNSRange(selectedTextRange, &range)) { - return; - } + GeckoTextMarkerRange markerRange = + [self textMarkerRangeFromRange:selectedTextRange]; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - textAcc->SetSelectionBoundsAt(0, range.location, - range.location + range.length); - } - } else { - mGeckoAccessible.AsProxy()->SetSelectionBoundsAt( - 0, range.location, range.location + range.length); - } + markerRange.Select(); } - (void)moxSetVisibleCharacterRange:(NSValue*)visibleCharacterRange { @@ -319,21 +273,13 @@ } - (NSString*)moxStringForRange:(NSValue*)range { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); + GeckoTextMarkerRange markerRange = [self textMarkerRangeFromRange:range]; - NSRange r = [range rangeValue]; - nsAutoString text; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - textAcc->TextSubstring(r.location, r.location + r.length, text); - } - } else { - mGeckoAccessible.AsProxy()->TextSubstring(r.location, r.location + r.length, - text); + if (!markerRange.IsValid()) { + return nil; } - return nsCocoaUtils::ToNSString(text); + return markerRange.Text(); } - (NSAttributedString*)moxAttributedStringForRange:(NSValue*)range { @@ -352,23 +298,13 @@ } - (NSValue*)moxBoundsForRange:(NSValue*)range { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); + GeckoTextMarkerRange markerRange = [self textMarkerRangeFromRange:range]; - NSRange r = [range rangeValue]; - int32_t start = r.location; - int32_t end = start + r.length; - DesktopIntRect bounds; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - bounds = DesktopIntRect::FromUnknownRect(textAcc->TextBounds(start, end)); - } - } else { - bounds = DesktopIntRect::FromUnknownRect( - mGeckoAccessible.AsProxy()->TextBounds(start, end)); + if (!markerRange.IsValid()) { + return nil; } - return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)]; + return markerRange.Bounds(); } #pragma mark - mozAccessible @@ -427,16 +363,7 @@ #pragma mark - - (long)textLength { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - - Accessible* acc = mGeckoAccessible.AsAccessible(); - ProxyAccessible* proxy = mGeckoAccessible.AsProxy(); - HyperTextAccessible* textAcc = acc ? acc->AsHyperText() : nullptr; - if (!textAcc && !proxy) { - return 0; - } - - return textAcc ? textAcc->CharacterCount() : proxy->CharacterCount(); + return [[self text] length]; } - (BOOL)isReadOnly { @@ -444,26 +371,42 @@ } - (NSString*)text { - MOZ_ASSERT(!mGeckoAccessible.IsNull()); - // A password text field returns an empty value if (mRole == roles::PASSWORD_TEXT) { return @""; } - nsAutoString text; - if (mGeckoAccessible.IsAccessible()) { - if (HyperTextAccessible* textAcc = - mGeckoAccessible.AsAccessible()->AsHyperText()) { - textAcc->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, - text); - } - } else { - mGeckoAccessible.AsProxy()->TextSubstring( - 0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text); + id delegate = [self moxTextMarkerDelegate]; + return [delegate + moxStringForTextMarkerRange:[delegate + moxTextMarkerRangeForUIElement:self]]; +} + +- (GeckoTextMarkerRange)selection { + MOZ_ASSERT(!mGeckoAccessible.IsNull()); + + id delegate = [self moxTextMarkerDelegate]; + GeckoTextMarkerRange selection = + [static_cast(delegate) selection]; + + if (!selection.Crop(mGeckoAccessible)) { + // The selection is not in this accessible. Return invalid range. + return GeckoTextMarkerRange(); } - return nsCocoaUtils::ToNSString(text); + return selection; +} + +- (GeckoTextMarkerRange)textMarkerRangeFromRange:(NSValue*)range { + NSRange r = [range rangeValue]; + + GeckoTextMarker startMarker = + GeckoTextMarker::MarkerFromIndex(mGeckoAccessible, r.location); + + GeckoTextMarker endMarker = + GeckoTextMarker::MarkerFromIndex(mGeckoAccessible, r.location + r.length); + + return GeckoTextMarkerRange(startMarker, endMarker); } @end diff -Nru firefox-84.0.2+build1/accessible/mac/Platform.mm firefox-85.0+build1/accessible/mac/Platform.mm --- firefox-84.0.2+build1/accessible/mac/Platform.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/Platform.mm 2021-01-18 19:58:08.000000000 +0000 @@ -17,6 +17,12 @@ #include "MOXWebAreaAccessible.h" #include "nsAppShell.h" +#include "mozilla/Telemetry.h" + +// Available from 10.13 onwards; test availability at runtime before using +@interface NSWorkspace (AvailableSinceHighSierra) +@property(readonly) BOOL isVoiceOverEnabled; +@end namespace mozilla { namespace a11y { @@ -77,14 +83,16 @@ } void ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType) { - // ignore everything but focus-changed, value-changed, caret, - // selection, and document load complete events for now. + // Ignore event that we don't escape below, they aren't yet supported. if (aEventType != nsIAccessibleEvent::EVENT_FOCUS && aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE && aEventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE && aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED && aEventType != nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE && - aEventType != nsIAccessibleEvent::EVENT_REORDER) + aEventType != nsIAccessibleEvent::EVENT_REORDER && + aEventType != nsIAccessibleEvent::EVENT_LIVE_REGION_ADDED && + aEventType != nsIAccessibleEvent::EVENT_LIVE_REGION_REMOVED && + aEventType != nsIAccessibleEvent::EVENT_NAME_CHANGE) return; mozAccessible* wrapper = GetNativeFromGeckoAccessible(aProxy); @@ -180,8 +188,18 @@ @implementation GeckoNSApplication (a11y) - (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute { - if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) + NSLog(@"Checking a11y"); + if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) { mozilla::a11y::sA11yShouldBeEnabled = ([value intValue] == 1); +#if defined(MOZ_TELEMETRY_REPORTING) + if ([[NSWorkspace sharedWorkspace] + respondsToSelector:@selector(isVoiceOverEnabled)] && + [[NSWorkspace sharedWorkspace] isVoiceOverEnabled]) { + Telemetry::ScalarSet(Telemetry::ScalarID::A11Y_INSTANTIATORS, + u"VoiceOver"_ns); + } +#endif // defined(MOZ_TELEMETRY_REPORTING) + } return [super accessibilitySetValue:value forAttribute:attribute]; } diff -Nru firefox-84.0.2+build1/accessible/mac/RotorRules.h firefox-85.0+build1/accessible/mac/RotorRules.h --- firefox-84.0.2+build1/accessible/mac/RotorRules.h 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/RotorRules.h 2021-01-18 19:58:08.000000000 +0000 @@ -57,6 +57,14 @@ virtual uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; }; +class RotorTextEntryRule final : public RotorRule { + public: + explicit RotorTextEntryRule(AccessibleOrProxy& aDirectDescendantsFrom); + explicit RotorTextEntryRule(); + + virtual uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; +}; + class RotorLinkRule : public RotorRule { public: explicit RotorLinkRule(); @@ -113,6 +121,15 @@ int32_t mLevel; }; +class RotorLiveRegionRule : public RotorRule { + public: + explicit RotorLiveRegionRule(AccessibleOrProxy& aDirectDescendantsFrom) + : RotorRule(aDirectDescendantsFrom) {} + explicit RotorLiveRegionRule() : RotorRule() {} + + uint16_t Match(const AccessibleOrProxy& aAccOrProxy) override; +}; + /** * This rule matches all accessibles with roles::OUTLINEITEM. If * outlines are nested, it ignores the nested subtree and returns diff -Nru firefox-84.0.2+build1/accessible/mac/RotorRules.mm firefox-85.0+build1/accessible/mac/RotorRules.mm --- firefox-84.0.2+build1/accessible/mac/RotorRules.mm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/mac/RotorRules.mm 2021-01-18 19:58:07.000000000 +0000 @@ -164,6 +164,31 @@ return result; } +// Rotor TextEntry Rule + +RotorTextEntryRule::RotorTextEntryRule( + AccessibleOrProxy& aDirectDescendantsFrom) + : RotorRule(aDirectDescendantsFrom){}; + +RotorTextEntryRule::RotorTextEntryRule() : RotorRule(){}; + +uint16_t RotorTextEntryRule::Match(const AccessibleOrProxy& aAccOrProxy) { + uint16_t result = RotorRule::Match(aAccOrProxy); + + // if a match was found in the base-class's Match function, + // it is valid to consider that match again here. if it is + // not of the desired role, we flip the match bit to "unmatch" + // otherwise, the match persists. + if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) { + if (aAccOrProxy.Role() != roles::PASSWORD_TEXT && + aAccOrProxy.Role() != roles::ENTRY) { + result &= ~nsIAccessibleTraversalRule::FILTER_MATCH; + } + } + + return result; +} + // Rotor Link Rule RotorLinkRule::RotorLinkRule(AccessibleOrProxy& aDirectDescendantsFrom) @@ -308,6 +333,18 @@ return result; } +uint16_t RotorLiveRegionRule::Match(const AccessibleOrProxy& aAccOrProxy) { + uint16_t result = RotorRule::Match(aAccOrProxy); + + if ((result & nsIAccessibleTraversalRule::FILTER_MATCH)) { + mozAccessible* nativeMatch = GetNativeFromGeckoAccessible(aAccOrProxy); + if (![nativeMatch moxIsLiveRegion]) { + result &= ~nsIAccessibleTraversalRule::FILTER_MATCH; + } + } + return result; +} + // Outline Rule OutlineRule::OutlineRule() : RotorRule(){}; diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_app.js firefox-85.0+build1/accessible/tests/browser/mac/browser_app.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_app.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_app.js 2021-01-18 19:58:08.000000000 +0000 @@ -159,3 +159,23 @@ } ); }); + +/** + * Tests for location bar + */ +add_task(async () => { + await BrowserTestUtils.withNewTab( + { + gBrowser, + url: "http://example.com", + }, + async browser => { + let input = await getMacAccessible("urlbar-input"); + is( + input.getAttributeValue("AXValue"), + "example.com", + "Location bar has correct value" + ); + } + ); +}); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser.ini firefox-85.0+build1/accessible/tests/browser/mac/browser.ini --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -4,6 +4,9 @@ head.js doc_aria_tabs.html doc_textmarker_test.html + doc_rich_listbox.xhtml + doc_menulist.xhtml + doc_tree.xhtml !/accessible/tests/browser/shared-head.js !/accessible/tests/browser/*.jsm !/accessible/tests/mochitest/*.js @@ -38,4 +41,8 @@ [browser_text_selection.js] [browser_navigate.js] [browser_outline.js] +[browser_outline_xul.js] [browser_hierarchy.js] +[browser_menulist.js] +[browser_rich_listbox.js] +[browser_live_regions.js] diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_input.js firefox-85.0+build1/accessible/tests/browser/mac/browser_input.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_input.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_input.js 2021-01-18 19:58:08.000000000 +0000 @@ -4,88 +4,208 @@ "use strict"; +async function testInput(browser, accDoc) { + let input = getNativeInterface(accDoc, "input"); + + is(input.getAttributeValue("AXDescription"), "Name", "Correct input label"); + is(input.getAttributeValue("AXTitle"), "", "Correct input title"); + is(input.getAttributeValue("AXValue"), "Elmer Fudd", "Correct input value"); + is( + input.getAttributeValue("AXNumberOfCharacters"), + 10, + "Correct length of value" + ); + + ok(input.attributeNames.includes("AXSelectedText"), "Has AXSelectedText"); + ok( + input.attributeNames.includes("AXSelectedTextRange"), + "Has AXSelectedTextRange" + ); + + let evt = Promise.all([ + waitForMacEvent("AXFocusedUIElementChanged", "input"), + waitForMacEvent("AXSelectedTextChanged", "body"), + waitForMacEvent("AXSelectedTextChanged", "input"), + ]); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("input").focus(); + }); + await evt; + + evt = Promise.all([ + waitForMacEvent("AXSelectedTextChanged", "body"), + waitForMacEvent("AXSelectedTextChanged", "input"), + ]); + await SpecialPowers.spawn(browser, [], () => { + let elm = content.document.getElementById("input"); + if (elm.setSelectionRange) { + elm.setSelectionRange(6, 9); + } else { + let r = new content.Range(); + let textNode = elm.firstElementChild.firstChild; + r.setStart(textNode, 6); + r.setEnd(textNode, 9); + + let s = content.getSelection(); + s.removeAllRanges(); + s.addRange(r); + } + }); + await evt; + + is( + input.getAttributeValue("AXSelectedText"), + "Fud", + "Correct text is selected" + ); + + Assert.deepEqual( + input.getAttributeValue("AXSelectedTextRange"), + [6, 3], + "correct range selected" + ); + + ok( + input.isAttributeSettable("AXSelectedTextRange"), + "AXSelectedTextRange is settable" + ); + + evt = Promise.all([ + waitForMacEvent("AXSelectedTextChanged"), + waitForMacEvent("AXSelectedTextChanged"), + ]); + input.setAttributeValue("AXSelectedTextRange", NSRange(1, 7)); + await evt; + + Assert.deepEqual( + input.getAttributeValue("AXSelectedTextRange"), + [1, 7], + "correct range selected" + ); + + is( + input.getAttributeValue("AXSelectedText"), + "lmer Fu", + "Correct text is selected" + ); + + let domSelection = await SpecialPowers.spawn(browser, [], () => { + let elm = content.document.querySelector("input#input"); + if (elm) { + return elm.value.substring(elm.selectionStart, elm.selectionEnd); + } + + return content.getSelection().toString(); + }); + + is(domSelection, "lmer Fu", "correct DOM selection"); + + is( + input.getParameterizedAttributeValue("AXStringForRange", NSRange(3, 5)), + "er Fu", + "AXStringForRange works" + ); +} + /** * Input selection test */ addAccessibleTask( ``, - async (browser, accDoc) => { - let input = getNativeInterface(accDoc, "input"); - - is(input.getAttributeValue("AXDescription"), "Name", "Correct input label"); - is(input.getAttributeValue("AXTitle"), "", "Correct input title"); - is( - input.getAttributeValue("AXNumberOfCharacters"), - 10, - "Correct length of value" - ); + testInput +); - ok(input.attributeNames.includes("AXSelectedText"), "Has AXSelecetdText"); - ok( - input.attributeNames.includes("AXSelectedTextRange"), - "Has AXSelecetdTextRange" - ); +/** + * contenteditable selection test + */ +addAccessibleTask( + `
+

Elmer Fudd

+
`, + testInput +); - // XXX: Need to look into why we send so many AXSelectedTextChanged notifications. +/** + * test contenteditable with selection that extends past editable part + */ +addAccessibleTask( + `Elmer Fudd is the name`, + async (browser, accDoc) => { let evt = Promise.all([ - waitForMacEvent("AXFocusedUIElementChanged"), - waitForMacEvent("AXSelectedTextChanged"), - waitForMacEvent("AXSelectedTextChanged"), - waitForMacEvent("AXSelectedTextChanged"), + waitForMacEvent("AXFocusedUIElementChanged", "input"), + waitForMacEvent("AXSelectedTextChanged", "body"), + waitForMacEvent("AXSelectedTextChanged", "input"), ]); await SpecialPowers.spawn(browser, [], () => { - let elm = content.document.getElementById("input"); - elm.focus(); - elm.setSelectionRange(6, 9); + content.document.getElementById("input").focus(); }); await evt; - is( - input.getAttributeValue("AXSelectedText"), - "Fud", - "Correct text is selected" - ); - - Assert.deepEqual( - input.getAttributeValue("AXSelectedTextRange"), - [6, 3], - "correct range selected" - ); - - ok( - input.isAttributeSettable("AXSelectedTextRange"), - "AXSelectedTextRange is settable" - ); + evt = waitForEvent(EVENT_TEXT_CARET_MOVED); + await SpecialPowers.spawn(browser, [], () => { + let input = content.document.getElementById("input"); + let notinput = content.document.getElementById("notinput"); - evt = Promise.all([ - waitForMacEvent("AXSelectedTextChanged"), - waitForMacEvent("AXSelectedTextChanged"), - ]); - input.setAttributeValue("AXSelectedTextRange", NSRange(1, 7)); + let r = new content.Range(); + r.setStart(input.firstChild, 4); + r.setEnd(notinput.firstChild, 6); + + let s = content.getSelection(); + s.removeAllRanges(); + s.addRange(r); + }); + await evt; - Assert.deepEqual( - input.getAttributeValue("AXSelectedTextRange"), - [1, 7], - "correct range selected" - ); + let input = getNativeInterface(accDoc, "input"); is( input.getAttributeValue("AXSelectedText"), - "lmer Fu", - "Correct text is selected" + "r Fudd", + "Correct text is selected in #input" ); - let domSelection = await SpecialPowers.spawn(browser, [], () => { - let elm = content.document.getElementById("input"); - return [elm.selectionStart, elm.selectionEnd]; - }); - - Assert.deepEqual(domSelection, [1, 8], "correct DOM selection"); - is( - input.getParameterizedAttributeValue("AXStringForRange", NSRange(3, 5)), - "er Fu", - "AXStringForRange works" + stringForRange( + input, + input.getAttributeValue("AXSelectedTextMarkerRange") + ), + "r Fudd is the", + "Correct text is selected in document" + ); + } +); + +/** + * test nested content editables and their ancestor getters. + */ +addAccessibleTask( + `
+

Bob Loblaw's

+
+ Law Blog +
+
`, + (browser, accDoc) => { + let link = getNativeInterface(accDoc, "link"); + let innerLink = getNativeInterface(accDoc, "inner_link"); + + let idmatches = (elem, id) => { + is(elem.getAttributeValue("AXDOMIdentifier"), id, "Matches ID"); + }; + + idmatches(link.getAttributeValue("AXEditableAncestor"), "outer"); + idmatches(link.getAttributeValue("AXFocusableAncestor"), "outer"); + idmatches(link.getAttributeValue("AXHighestEditableAncestor"), "outer"); + + idmatches(innerLink.getAttributeValue("AXEditableAncestor"), "inner"); + idmatches(innerLink.getAttributeValue("AXFocusableAncestor"), "inner"); + idmatches( + innerLink.getAttributeValue("AXHighestEditableAncestor"), + "outer" ); } ); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_live_regions.js firefox-85.0+build1/accessible/tests/browser/mac/browser_live_regions.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_live_regions.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_live_regions.js 2021-01-18 19:58:07.000000000 +0000 @@ -0,0 +1,165 @@ +/* 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/. */ + +"use strict"; + +/** + * Test live region creation and removal. + */ +addAccessibleTask( + ` +
Polite region
+
Assertive region
+ `, + async (browser, accDoc) => { + let politeRegion = getNativeInterface(accDoc, "polite"); + ok( + !politeRegion.attributeNames.includes("AXARIALive"), + "region is not live" + ); + + let liveRegionAdded = waitForMacEvent("AXLiveRegionCreated", "polite"); + await SpecialPowers.spawn(browser, [], () => { + content.document + .getElementById("polite") + .setAttribute("aria-atomic", "true"); + content.document + .getElementById("polite") + .setAttribute("aria-live", "polite"); + }); + await liveRegionAdded; + is( + politeRegion.getAttributeValue("AXARIALive"), + "polite", + "region is now live" + ); + ok(politeRegion.getAttributeValue("AXARIAAtomic"), "region is atomic"); + is( + politeRegion.getAttributeValue("AXARIARelevant"), + "removals", + "region has defined aria-relevant" + ); + + let assertiveRegion = getNativeInterface(accDoc, "assertive"); + is( + assertiveRegion.getAttributeValue("AXARIALive"), + "assertive", + "region is assertive" + ); + ok( + !assertiveRegion.getAttributeValue("AXARIAAtomic"), + "region is not atomic" + ); + is( + assertiveRegion.getAttributeValue("AXARIARelevant"), + "additions text", + "region has default aria-relevant" + ); + + let liveRegionRemoved = waitForEvent( + EVENT_LIVE_REGION_REMOVED, + "assertive" + ); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("assertive").removeAttribute("aria-live"); + }); + await liveRegionRemoved; + ok(!assertiveRegion.getAttributeValue("AXARIALive"), "region is not live"); + + liveRegionAdded = waitForMacEvent("AXLiveRegionCreated", "new-region"); + await SpecialPowers.spawn(browser, [], () => { + let newRegionElm = content.document.createElement("div"); + newRegionElm.id = "new-region"; + newRegionElm.setAttribute("aria-live", "assertive"); + content.document.body.appendChild(newRegionElm); + }); + await liveRegionAdded; + + let newRegion = getNativeInterface(accDoc, "new-region"); + is( + newRegion.getAttributeValue("AXARIALive"), + "assertive", + "region is assertive" + ); + + let loadComplete = Promise.all([ + waitForMacEvent("AXLoadComplete"), + waitForMacEvent("AXLiveRegionCreated", "region-1"), + waitForMacEvent("AXLiveRegionCreated", "region-2"), + waitForMacEvent("AXLiveRegionCreated", "status"), + waitForMacEvent("AXLiveRegionCreated", "output"), + ]); + + await SpecialPowers.spawn(browser, [], () => { + content.location = `data:text/html;charset=utf-8, +
+
+
+ +
+ `; + }); + let webArea = (await loadComplete)[0]; + + is(webArea.getAttributeValue("AXRole"), "AXWebArea", "web area yeah"); + const searchPred = { + AXSearchKey: "AXLiveRegionSearchKey", + AXResultsLimit: -1, + AXDirection: "AXDirectionNext", + }; + const liveRegions = webArea.getParameterizedAttributeValue( + "AXUIElementsForSearchPredicate", + NSDictionary(searchPred) + ); + Assert.deepEqual( + liveRegions.map(r => r.getAttributeValue("AXDOMIdentifier")), + ["region-1", "region-2", "alert", "status", "output"], + "SearchPredicate returned all live regions" + ); + } +); + +/** + * Test live region changes + */ +addAccessibleTask( + ` +
+ The time is 4:55pm + + +
+ `, + async (browser, accDoc) => { + let liveRegionChanged = waitForMacEvent("AXLiveRegionChanged", "live"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("time").textContent = "4:56pm"; + }); + await liveRegionChanged; + ok(true, "changed textContent"); + + liveRegionChanged = waitForMacEvent("AXLiveRegionChanged", "live"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("p").style.display = "block"; + }); + await liveRegionChanged; + ok(true, "changed display style to block"); + + liveRegionChanged = waitForMacEvent("AXLiveRegionChanged", "live"); + await SpecialPowers.spawn(browser, [], () => { + content.document.getElementById("p").style.display = "none"; + }); + await liveRegionChanged; + ok(true, "changed display style to none"); + + liveRegionChanged = waitForMacEvent("AXLiveRegionChanged", "live"); + await SpecialPowers.spawn(browser, [], () => { + content.document + .getElementById("button") + .setAttribute("aria-label", "Stop"); + }); + await liveRegionChanged; + ok(true, "changed aria-label"); + } +); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_menulist.js firefox-85.0+build1/accessible/tests/browser/mac/browser_menulist.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_menulist.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_menulist.js 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1,52 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/attributes.js */ +/* import-globals-from ../../mochitest/role.js */ +/* import-globals-from ../../mochitest/states.js */ +loadScripts( + { name: "role.js", dir: MOCHITESTS_DIR }, + { name: "states.js", dir: MOCHITESTS_DIR }, + { name: "attributes.js", dir: MOCHITESTS_DIR } +); + +addAccessibleTask( + "mac/doc_menulist.xhtml", + async (browser, accDoc) => { + const menulist = getNativeInterface(accDoc, "defaultZoom"); + + let actions = menulist.actionNames; + ok(actions.includes("AXPress"), "menu has press action"); + + let event = waitForMacEvent("AXMenuOpened"); + menulist.performAction("AXPress"); + const menupopup = await event; + + const menuItems = menupopup.getAttributeValue("AXChildren"); + is(menuItems.length, 4, "Found four children in menulist"); + is( + menuItems[0].getAttributeValue("AXTitle"), + "50%", + "First item has correct title" + ); + is( + menuItems[1].getAttributeValue("AXTitle"), + "100%", + "Second item has correct title" + ); + is( + menuItems[2].getAttributeValue("AXTitle"), + "150%", + "Third item has correct title" + ); + is( + menuItems[3].getAttributeValue("AXTitle"), + "200%", + "Fourth item has correct title" + ); + }, + { topLevel: false, chrome: true } +); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_outline.js firefox-85.0+build1/accessible/tests/browser/mac/browser_outline.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_outline.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_outline.js 2021-01-18 19:58:08.000000000 +0000 @@ -80,8 +80,8 @@ ); is( outRows[0].getAttributeValue("AXDisclosureLevel"), - 1, - "Row is level one" + 0, + "Row is level zero" ); is(outRows[1].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); @@ -97,8 +97,8 @@ ); is( outRows[1].getAttributeValue("AXDisclosureLevel"), - 1, - "Row is level one" + 0, + "Row is level zero" ); is( @@ -118,8 +118,8 @@ ); is( outRows[2].getAttributeValue("AXDisclosureLevel"), - 1, - "Row is level one" + 0, + "Row is level zero" ); is(outRows[3].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); @@ -137,8 +137,8 @@ ); is( outRows[3].getAttributeValue("AXDisclosureLevel"), - 2, - "Row is level two" + 1, + "Row is level one" ); } ); @@ -260,8 +260,8 @@ ); is( outRows[0].getAttributeValue("AXDisclosureLevel"), - 1, - "Row is level one" + 0, + "Row is level zero" ); is(outRows[2].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); @@ -277,8 +277,8 @@ ); is( outRows[2].getAttributeValue("AXDisclosureLevel"), - 2, - "Row is level two" + 1, + "Row is level one" ); is( @@ -301,8 +301,8 @@ ); is( outRows[3].getAttributeValue("AXDisclosureLevel"), - 3, - "Row is level three" + 2, + "Row is level two" ); is( @@ -322,8 +322,8 @@ ); is( outRows[5].getAttributeValue("AXDisclosureLevel"), - 1, - "Row is level one" + 0, + "Row is level zero" ); is(outRows[6].getAttributeValue("AXDisclosing"), 1, "Row is disclosing"); @@ -341,8 +341,8 @@ ); is( outRows[6].getAttributeValue("AXDisclosureLevel"), - 2, - "Row is level two" + 1, + "Row is level one" ); is( @@ -364,8 +364,8 @@ ); is( outRows[7].getAttributeValue("AXDisclosureLevel"), - 3, - "Row is level three" + 2, + "Row is level two" ); } ); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_outline_xul.js firefox-85.0+build1/accessible/tests/browser/mac/browser_outline_xul.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_outline_xul.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_outline_xul.js 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1,274 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/attributes.js */ +loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "mac/doc_tree.xhtml", + async (browser, accDoc) => { + const tree = getNativeInterface(accDoc, "tree"); + is( + tree.getAttributeValue("AXRole"), + "AXOutline", + "Found tree with role outline" + ); + // XUL trees store all rows as direct children of the outline, + // so we should see nine here instead of just three: + // (Groceries, Fruits, Veggies) + const treeChildren = tree.getAttributeValue("AXChildren"); + is(treeChildren.length, 9, "Found nine direct children"); + + const treeCols = tree.getAttributeValue("AXColumns"); + is(treeCols.length, 1, "Found one column in tree"); + + // Here, we should get only outline rows, not the title + const treeRows = tree.getAttributeValue("AXRows"); + is(treeRows.length, 8, "Found 8 total rows"); + + is( + treeRows[0].getAttributeValue("AXDescription"), + "Fruits", + "Located correct first row, row has correct desc" + ); + is( + treeRows[0].getAttributeValue("AXDisclosing"), + 1, + "Fruits is disclosing" + ); + is( + treeRows[0].getAttributeValue("AXDisclosedByRow"), + null, + "Fruits is disclosed by outline" + ); + is( + treeRows[0].getAttributeValue("AXDisclosureLevel"), + 0, + "Fruits is level zero" + ); + let disclosedRows = treeRows[0].getAttributeValue("AXDisclosedRows"); + is(disclosedRows.length, 2, "Fruits discloses two rows"); + is( + disclosedRows[0].getAttributeValue("AXDescription"), + "Apple", + "fruits discloses apple" + ); + is( + disclosedRows[1].getAttributeValue("AXDescription"), + "Orange", + "fruits discloses orange" + ); + + is( + treeRows[1].getAttributeValue("AXDescription"), + "Apple", + "Located correct second row, row has correct desc" + ); + is( + treeRows[1].getAttributeValue("AXDisclosing"), + 0, + "Apple is not disclosing" + ); + is( + treeRows[1] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Fruits", + "Apple is disclosed by fruits" + ); + is( + treeRows[1].getAttributeValue("AXDisclosureLevel"), + 1, + "Apple is level one" + ); + is( + treeRows[1].getAttributeValue("AXDisclosedRows").length, + 0, + "Apple does not disclose rows" + ); + + is( + treeRows[2].getAttributeValue("AXDescription"), + "Orange", + "Located correct third row, row has correct desc" + ); + is( + treeRows[2].getAttributeValue("AXDisclosing"), + 0, + "Orange is not disclosing" + ); + is( + treeRows[2] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Fruits", + "Orange is disclosed by fruits" + ); + is( + treeRows[2].getAttributeValue("AXDisclosureLevel"), + 1, + "Orange is level one" + ); + is( + treeRows[2].getAttributeValue("AXDisclosedRows").length, + 0, + "Orange does not disclose rows" + ); + + is( + treeRows[3].getAttributeValue("AXDescription"), + "Veggies", + "Located correct fourth row, row has correct desc" + ); + is( + treeRows[3].getAttributeValue("AXDisclosing"), + 1, + "Veggies is disclosing" + ); + is( + treeRows[3].getAttributeValue("AXDisclosedByRow"), + null, + "Veggies is disclosed by outline" + ); + is( + treeRows[3].getAttributeValue("AXDisclosureLevel"), + 0, + "Veggies is level zero" + ); + disclosedRows = treeRows[3].getAttributeValue("AXDisclosedRows"); + is(disclosedRows.length, 2, "Veggies discloses two rows"); + is( + disclosedRows[0].getAttributeValue("AXDescription"), + "Green Veggies", + "Veggies discloses green veggies" + ); + is( + disclosedRows[1].getAttributeValue("AXDescription"), + "Squash", + "Veggies discloses squash" + ); + + is( + treeRows[4].getAttributeValue("AXDescription"), + "Green Veggies", + "Located correct fifth row, row has correct desc" + ); + is( + treeRows[4].getAttributeValue("AXDisclosing"), + 1, + "Green veggies is disclosing" + ); + is( + treeRows[4] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Veggies", + "Green Veggies is disclosed by veggies" + ); + is( + treeRows[4].getAttributeValue("AXDisclosureLevel"), + 1, + "Green veggies is level one" + ); + disclosedRows = treeRows[4].getAttributeValue("AXDisclosedRows"); + is(disclosedRows.length, 2, "Green veggies has two rows"); + is( + disclosedRows[0].getAttributeValue("AXDescription"), + "Spinach", + "Green veggies discloses spinach" + ); + is( + disclosedRows[1].getAttributeValue("AXDescription"), + "Peas", + "Green veggies discloses peas" + ); + + is( + treeRows[5].getAttributeValue("AXDescription"), + "Spinach", + "Located correct sixth row, row has correct desc" + ); + is( + treeRows[5].getAttributeValue("AXDisclosing"), + 0, + "Spinach is not disclosing" + ); + is( + treeRows[5] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Green Veggies", + "Spinach is disclosed by green veggies" + ); + is( + treeRows[5].getAttributeValue("AXDisclosureLevel"), + 2, + "Spinach is level two" + ); + is( + treeRows[5].getAttributeValue("AXDisclosedRows").length, + 0, + "Spinach does not disclose rows" + ); + + is( + treeRows[6].getAttributeValue("AXDescription"), + "Peas", + "Located correct seventh row, row has correct desc" + ); + is( + treeRows[6].getAttributeValue("AXDisclosing"), + 0, + "Peas is not disclosing" + ); + is( + treeRows[6] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Green Veggies", + "Peas is disclosed by green veggies" + ); + is( + treeRows[6].getAttributeValue("AXDisclosureLevel"), + 2, + "Peas is level two" + ); + is( + treeRows[6].getAttributeValue("AXDisclosedRows").length, + 0, + "Peas does not disclose rows" + ); + + is( + treeRows[7].getAttributeValue("AXDescription"), + "Squash", + "Located correct eighth row, row has correct desc" + ); + is( + treeRows[7].getAttributeValue("AXDisclosing"), + 0, + "Squash is not disclosing" + ); + is( + treeRows[7] + .getAttributeValue("AXDisclosedByRow") + .getAttributeValue("AXDescription"), + "Veggies", + "Squash is disclosed by veggies" + ); + is( + treeRows[7].getAttributeValue("AXDisclosureLevel"), + 1, + "Squash is level one" + ); + is( + treeRows[7].getAttributeValue("AXDisclosedRows").length, + 0, + "Squash does not disclose rows" + ); + }, + { topLevel: false, chrome: true } +); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_popupbutton.js firefox-85.0+build1/accessible/tests/browser/mac/browser_popupbutton.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_popupbutton.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_popupbutton.js 2021-01-18 19:58:08.000000000 +0000 @@ -69,12 +69,8 @@ let menuParent = menu.getAttributeValue("AXParent"); is( menuParent.getAttributeValue("AXRole"), - "AXGroup", - "dropdown parent is a group" - ); - ok( - menuParent.attributeNames.includes("AXMain"), - "group is main/root element" + "AXPopUpButton", + "dropdown parent is a popup button" ); let menuItems = menu.getAttributeValue("AXChildren").map(c => { diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_rich_listbox.js firefox-85.0+build1/accessible/tests/browser/mac/browser_rich_listbox.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_rich_listbox.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_rich_listbox.js 2021-01-18 19:58:07.000000000 +0000 @@ -0,0 +1,73 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from ../../mochitest/attributes.js */ +loadScripts({ name: "attributes.js", dir: MOCHITESTS_DIR }); + +addAccessibleTask( + "mac/doc_rich_listbox.xhtml", + async (browser, accDoc) => { + const categories = getNativeInterface(accDoc, "categories"); + const categoriesChildren = categories.getAttributeValue("AXChildren"); + is(categoriesChildren.length, 4, "Found listbox and 4 items"); + + const general = getNativeInterface(accDoc, "general"); + is( + general.getAttributeValue("AXTitle"), + "general", + "general has appropriate title" + ); + is( + categoriesChildren[0].getAttributeValue("AXTitle"), + general.getAttributeValue("AXTitle"), + "Found general listitem" + ); + is( + general.getAttributeValue("AXEnabled"), + 1, + "general is enabled, not dimmed" + ); + + const home = getNativeInterface(accDoc, "home"); + is(home.getAttributeValue("AXTitle"), "home", "home has appropriate title"); + is( + categoriesChildren[1].getAttributeValue("AXTitle"), + home.getAttributeValue("AXTitle"), + "Found home listitem" + ); + is(home.getAttributeValue("AXEnabled"), 1, "Home is enabled, not dimmed"); + + const search = getNativeInterface(accDoc, "search"); + is( + search.getAttributeValue("AXTitle"), + "search", + "search has appropriate title" + ); + is( + categoriesChildren[2].getAttributeValue("AXTitle"), + search.getAttributeValue("AXTitle"), + "Found search listitem" + ); + is( + search.getAttributeValue("AXEnabled"), + 1, + "search is enabled, not dimmed" + ); + + const privacy = getNativeInterface(accDoc, "privacy"); + is( + privacy.getAttributeValue("AXTitle"), + "privacy", + "privacy has appropriate title" + ); + is( + categoriesChildren[3].getAttributeValue("AXTitle"), + privacy.getAttributeValue("AXTitle"), + "Found privacy listitem" + ); + }, + { topLevel: false, chrome: true } +); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_roles_elements.js firefox-85.0+build1/accessible/tests/browser/mac/browser_roles_elements.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_roles_elements.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_roles_elements.js 2021-01-18 19:58:07.000000000 +0000 @@ -73,12 +73,21 @@
+ +
+
+ + + + + + Deleted text
term
definition
@@ -172,12 +181,21 @@ testRoleAndSubRole(accDoc, "timer", null, "AXApplicationTimer"); testRoleAndSubRole(accDoc, "tooltip", "AXGroup", "AXUserInterfaceTooltip"); + // Text boxes + testRoleAndSubRole(accDoc, "textbox_multiline", "AXTextArea"); + testRoleAndSubRole(accDoc, "textbox_singleline", "AXTextArea"); + testRoleAndSubRole(accDoc, "textArea", "AXTextArea"); + testRoleAndSubRole(accDoc, "textInput", "AXTextField"); + // True HTML5 search field testRoleAndSubRole(accDoc, "htmlSearch", "AXTextField", "AXSearchField"); // A button morphed into a toggle by ARIA testRoleAndSubRole(accDoc, "toggle", "AXCheckBox", "AXToggle"); + // A banana button + testRoleAndSubRole(accDoc, "banana", "AXButton", null, "banana"); + // Other elements testRoleAndSubRole(accDoc, "deletion", "AXGroup", "AXDeleteStyleGroup"); testRoleAndSubRole(accDoc, "dl", "AXList", "AXDescriptionList"); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_rotor.js firefox-85.0+build1/accessible/tests/browser/mac/browser_rotor.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_rotor.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_rotor.js 2021-01-18 19:58:07.000000000 +0000 @@ -1040,11 +1040,10 @@ "AXUIElementsForSearchPredicate", NSDictionary(searchPred) ); - is(results.length, 1, "WebArea is before root group"); is( - results[0].getAttributeValue("AXRole"), - "AXWebArea", - "Got web area accessible" + results.length, + 0, + "Searching backwards from root group should yield no results" ); const rootGroup = webArea.getAttributeValue("AXChildren")[0]; diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_text_basics.js firefox-85.0+build1/accessible/tests/browser/mac/browser_text_basics.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_text_basics.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_text_basics.js 2021-01-18 19:58:08.000000000 +0000 @@ -9,7 +9,14 @@ is(stringForRange(macDoc, range), expected, msg); } -function testUIElement(macDoc, marker, msg, expectedRole, expectedValue) { +function testUIElement( + macDoc, + marker, + msg, + expectedRole, + expectedValue, + expectedRange +) { let elem = macDoc.getParameterizedAttributeValue( "AXUIElementForTextMarker", marker @@ -26,7 +33,7 @@ ); is( stringForRange(macDoc, elemRange), - expectedValue, + expectedRange, `${msg}: element range matches element value` ); } @@ -238,3 +245,21 @@ is(stringForRange(macDoc, range), "ello good"); } ); + +addAccessibleTask( + `goodbye`, + async (browser, accDoc) => { + let macDoc = accDoc.nativeInterface.QueryInterface( + Ci.nsIAccessibleMacInterface + ); + + let input = getNativeInterface(accDoc, "input"); + + let range = macDoc.getParameterizedAttributeValue( + "AXTextMarkerRangeForUIElement", + input + ); + + is(stringForRange(macDoc, range), "", "string value is correct"); + } +); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/browser_toggle_radio_check.js firefox-85.0+build1/accessible/tests/browser/mac/browser_toggle_radio_check.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/browser_toggle_radio_check.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/browser_toggle_radio_check.js 2021-01-18 19:58:08.000000000 +0000 @@ -23,10 +23,14 @@ let actions = checkbox.actionNames; ok(actions.includes("AXPress"), "Has press action"); + let evt = waitForMacEvent("AXValueChanged", "vehicle"); checkbox.performAction("AXPress"); + await evt; is(checkbox.getAttributeValue("AXValue"), 1, "Correct checked value"); + evt = waitForMacEvent("AXValueChanged", "vehicle"); checkbox.performAction("AXPress"); + await evt; is(checkbox.getAttributeValue("AXValue"), 0, "Correct checked value"); } ); @@ -52,10 +56,14 @@ let actions = toggle.actionNames; ok(actions.includes("AXPress"), "Has press action"); + let evt = waitForMacEvent("AXValueChanged", "toggle"); toggle.performAction("AXPress"); + await evt; is(toggle.getAttributeValue("AXValue"), 1, "Correct checked value"); + evt = waitForMacEvent("AXValueChanged", "toggle"); toggle.performAction("AXPress"); + await evt; is(toggle.getAttributeValue("AXValue"), 0, "Correct checked value"); } ); @@ -82,10 +90,14 @@ let actions = checkbox.actionNames; ok(actions.includes("AXPress"), "Has press action"); + let evt = waitForMacEvent("AXValueChanged", "checkbox"); checkbox.performAction("AXPress"); + await evt; is(checkbox.getAttributeValue("AXValue"), 1, "Correct checked value"); + evt = waitForMacEvent("AXValueChanged", "checkbox"); checkbox.performAction("AXPress"); + await evt; is(checkbox.getAttributeValue("AXValue"), 2, "Correct checked value"); } ); @@ -112,9 +124,12 @@ let actions = dewey.actionNames; ok(actions.includes("AXPress"), "Has press action"); - let stateChanged = waitForEvent(EVENT_STATE_CHANGE, "huey"); + let evt = Promise.all([ + waitForMacEvent("AXValueChanged", "huey"), + waitForMacEvent("AXValueChanged", "dewey"), + ]); dewey.performAction("AXPress"); - await stateChanged; + await evt; is( dewey.getAttributeValue("AXValue"), 1, diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/doc_menulist.xhtml firefox-85.0+build1/accessible/tests/browser/mac/doc_menulist.xhtml --- firefox-84.0.2+build1/accessible/tests/browser/mac/doc_menulist.xhtml 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/doc_menulist.xhtml 2021-01-18 19:58:07.000000000 +0000 @@ -0,0 +1,19 @@ + + + + + + + diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/doc_rich_listbox.xhtml firefox-85.0+build1/accessible/tests/browser/mac/doc_rich_listbox.xhtml --- firefox-84.0.2+build1/accessible/tests/browser/mac/doc_rich_listbox.xhtml 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/doc_rich_listbox.xhtml 2021-01-18 19:58:07.000000000 +0000 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/doc_textmarker_test.html firefox-85.0+build1/accessible/tests/browser/mac/doc_textmarker_test.html --- firefox-84.0.2+build1/accessible/tests/browser/mac/doc_textmarker_test.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/doc_textmarker_test.html 2021-01-18 19:58:07.000000000 +0000 @@ -25,1996 +25,2400 @@ "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bob", "Bob"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bob", "Bob"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bob", "Bob"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bob", " "], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: [" ", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", "Loblaw"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Loblaw", " "], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: [" ", "Lobs"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Lobs", "Lobs"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Lobs", "Lobs"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Lobs", "Lobs"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Lobs", " "], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: [" ", "Law"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Law", "Law"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Law", "Law"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Law", " "], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: [" ", "Bomb"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bomb", "Bomb"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bomb", "Bomb"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "Bob Loblaw Lobs Law Bomb", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb"], words: ["Bomb", "Bomb"], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "Bob Loblaw Lobs Law Bomb", paragraph: "I love all of my children equally", lines: ["Bob Loblaw Lobs Law Bomb", "Bob Loblaw Lobs Law Bomb", "I love all of my children equally"], words: ["Bomb", ""], - element: ["AXStaticText", "Bob Loblaw Lobs Law Bomb"] }, + element: ["AXStaticText", + "Bob Loblaw Lobs Law Bomb", + "Bob Loblaw Lobs Law Bomb"] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["I", " "], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "love"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["love", "love"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["love", "love"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["love", "love"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["love", " "], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "all"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["all", "all"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["all", "all"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["all", " "], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "of"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["of", "of"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["of", " "], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "my"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["my", "my"], - element: ["AXStaticText", "I love all of my "] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["my", " "], - element: ["AXStaticText", "I love all of my "] }, - { style: "children", + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, + { style: "I love all of my ", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "I love all of my ", "I love all of my "] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", "children"], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: "children", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["children", " "], - element: ["AXStaticText", "children"] }, + element: ["AXStaticText", "children", "children"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: [" ", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "I love all of my children equally", lines: ["I love all of my children equally", "I love all of my children equally", "I love all of my children equally"], words: ["equally", "equally"], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: " equally", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["I love all of my children equally", "I love all of my children equally", "This is the best free scrapbooking class I have ever taken"], words: ["equally", ""], - element: ["AXStaticText", " equally"] }, + element: ["AXStaticText", " equally", " equally"] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["This", "This"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["This", "This"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["This", "This"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["This", " "], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "is"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["is", "is"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["is", " "], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "the"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["the", "the"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["the", "the"], - element: ["AXStaticText", "This is the "] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["the", " "], - element: ["AXStaticText", "This is the "] }, - { style: "best", + element: ["AXStaticText", "This is the ", "This is the "] }, + { style: "This is the ", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "best"], - element: ["AXStaticText", "best"] }, + element: ["AXStaticText", "This is the ", "This is the "] }, { style: "best", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["best", "best"], - element: ["AXStaticText", "best"] }, + element: ["AXStaticText", "best", "best"] }, { style: "best", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["best", "best"], - element: ["AXStaticText", "best"] }, + element: ["AXStaticText", "best", "best"] }, { style: "best", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["best", "best"], - element: ["AXStaticText", "best"] }, - { style: " free scr", + element: ["AXStaticText", "best", "best"] }, + { style: "best", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["best", " "], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", "best", "best"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "free"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["free", "free"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["free", "free"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["free", "free"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["free", " "], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "scrapbooking"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", " free scr"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", " free scr"] }, - { style: "apbook", + element: ["AXStaticText", " free scr", " free scr"] }, + { style: " free scr", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", " free scr", " free scr"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "apbook", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "apbook"] }, + element: ["AXStaticText", "apbook", "apbook"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", "scrapbooking"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["scrapbooking", " "], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "class"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["class", "class"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["class", "class"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["class", "class"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["class", "class"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["class", " "], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "I"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["I", " "], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "have"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["have", "have"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["have", "have"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["have", "have"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["have", " "], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "ever"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["ever", "ever"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["ever", "ever"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["ever", "ever"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["ever", " "], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: [" ", "taken"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["taken", "taken"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["taken", "taken"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["taken", "taken"], - element: ["AXStaticText", "ing class I have ever taken"] }, + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, { style: "ing class I have ever taken", paragraph: "This is the best free scrapbooking class I have ever taken", lines: ["This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken", "This is the best free scrapbooking class I have ever taken"], words: ["taken", "taken"], - element: ["AXStaticText", "ing class I have ever taken"] }, - { style: "ing class I have ever taken", - paragraph: "Fried cheese with club sauce", - lines: ["This is the best free scrapbooking class I have ever taken", - "This is the best free scrapbooking class I have ever taken", - "Fried cheese with club sauce"], - words: ["taken", "\u2022 "], - element: ["AXStaticText", "ing class I have ever taken"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], - words: ["Fried", "Fried"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], - words: ["Fried", "Fried"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], - words: ["Fried", "Fried"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], - words: ["Fried", "Fried"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], - words: ["Fried", " "], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, + { style: "ing class I have ever taken", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["This is the best free scrapbooking class I have ever taken", + "This is the best free scrapbooking class I have ever taken", + "\u2022 Fried cheese with club sauce"], + words: ["taken", ""], + element: ["AXStaticText", + "ing class I have ever taken", + "ing class I have ever taken"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], + words: ["\u2022 Fried", "\u2022 Fried"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], + words: ["\u2022 Fried", "\u2022 Fried"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], + words: ["\u2022 Fried", "\u2022 Fried"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], + words: ["\u2022 Fried", "\u2022 Fried"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], + words: ["\u2022 Fried", " "], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: [" ", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", "cheese"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["cheese", " "], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: [" ", "with"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["with", " "], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: [" ", "club"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["club", " "], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: [" ", "sauce"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Fried cheese with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Fried cheese with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Fried cheese with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Fried cheese with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Fried cheese with club sauce", - "Fried cheese with club sauce", - "Popcorn shrimp with club sauce"], - words: ["sauce", "\u2022 "], - element: ["AXStaticText", "Fried cheese with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", "Popcorn"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], - words: ["Popcorn", " "], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Fried cheese with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["sauce", ""], + element: ["AXStaticText", + "Fried cheese with club sauce", + "\u2022 Fried cheese with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", "\u2022 Popcorn"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], + words: ["\u2022 Popcorn", " "], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: [" ", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", "shrimp"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["shrimp", " "], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: [" ", "with"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["with", " "], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: [" ", "club"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["club", "club"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["club", " "], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: [" ", "sauce"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Popcorn shrimp with club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Popcorn shrimp with club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Popcorn shrimp with club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Popcorn shrimp with club sauce", - "Popcorn shrimp with club sauce", - "Chicken fingers with spicy club sauce"], - words: ["sauce", "\u2022 "], - element: ["AXStaticText", "Popcorn shrimp with club sauce"] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", "Chicken"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], - words: ["Chicken", " "], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Popcorn shrimp with club sauce", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["sauce", ""], + element: ["AXStaticText", + "Popcorn shrimp with club sauce", + "\u2022 Popcorn shrimp with club sauce"] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", "\u2022 Chicken"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], + words: ["\u2022 Chicken", " "], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: [" ", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", "fingers"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["fingers", " "], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: [" ", "with"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["with", "with"], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "Chicken fingers with ", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["with", " "], - element: ["AXStaticText", "Chicken fingers with "] }, - { style: "spicy", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, + { style: "\u2022 Chicken fingers with ", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: [" ", "spicy"], - element: ["AXStaticText", "spicy"] }, + element: ["AXStaticText", + "Chicken fingers with ", + "\u2022 Chicken fingers with "] }, { style: "spicy", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["spicy", "spicy"], - element: ["AXStaticText", "spicy"] }, + element: ["AXStaticText", "spicy", "spicy"] }, { style: "spicy", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["spicy", "spicy"], - element: ["AXStaticText", "spicy"] }, + element: ["AXStaticText", "spicy", "spicy"] }, { style: "spicy", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["spicy", "spicy"], - element: ["AXStaticText", "spicy"] }, + element: ["AXStaticText", "spicy", "spicy"] }, { style: "spicy", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["spicy", "spicy"], - element: ["AXStaticText", "spicy"] }, - { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + element: ["AXStaticText", "spicy", "spicy"] }, + { style: "spicy", + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["spicy", " "], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", "spicy", "spicy"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: [" ", "club"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["club", "club"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["club", "club"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["club", "club"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["club", " "], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: [" ", "sauce"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", - paragraph: "Chicken fingers with spicy club sauce", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce"], + paragraph: "\u2022 Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce"], words: ["sauce", "sauce"], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: " club sauce", paragraph: "Do not order the Skip's Scramble", - lines: ["Chicken fingers with spicy club sauce", - "Chicken fingers with spicy club sauce", + lines: ["\u2022 Chicken fingers with spicy club sauce", + "\u2022 Chicken fingers with spicy club sauce", "Do not order the Skip's Scramble"], words: ["sauce", ""], - element: ["AXStaticText", " club sauce"] }, + element: ["AXStaticText", " club sauce", " club sauce"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Do", "Do"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Do", " "], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: [" ", "not"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["not", "not"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["not", "not"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["not", " "], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: [" ", "order"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["order", "order"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["order", "order"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["order", "order"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["order", "order"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["order", " "], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: [" ", "the"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["the", "the"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["the", "the"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["the", " "], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: [" ", "Skip'"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Skip'", "Skip'"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Skip'", "Skip'"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Skip'", "Skip'"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Skip'", "'"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Skip'", "s"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["s", " "], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: [" ", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "Do not order the Skip's Scramble", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "Do not order the Skip's Scramble"], words: ["Scramble", "Scramble"], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "Do not order the Skip's Scramble", paragraph: "These are my awards, Mother. From Army.", lines: ["Do not order the Skip's Scramble", "Do not order the Skip's Scramble", "These "], words: ["Scramble", ""], - element: ["AXStaticText", "Do not order the Skip's Scramble"] }, + element: ["AXStaticText", + "Do not order the Skip's Scramble", + "Do not order the Skip's Scramble"] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["These ", "These ", "These "], words: ["These", "These"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["These ", "These ", "These "], words: ["These", "These"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["These ", "These ", "These "], words: ["These", "These"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["These ", "These ", "These "], words: ["These", "These"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["These ", "These ", "These "], words: ["These", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["are ", "are ", "are "], words: [" ", "are"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["are ", "are ", "are "], words: ["are", "are"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["are ", "are ", "are "], words: ["are", "are"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["are ", "are ", "are "], words: ["are", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["my ", "my ", "my "], words: [" ", "my"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["my ", "my ", "my "], words: ["my", "my"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["my ", "my ", "my "], words: ["my", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: [" ", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards,"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", "awards, "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["awards, ", "awards, ", "awards, "], words: ["awards,", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: [" ", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", "Mother. "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Mother. ", "Mother. ", "Mother. "], words: ["Mother.", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["From ", "From ", "From "], words: [" ", "From"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["From ", "From ", "From "], words: ["From", "From"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["From ", "From ", "From "], words: ["From", "From"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["From ", "From ", "From "], words: ["From", "From"], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["From ", "From ", "From "], words: ["From", " "], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Army.", "Army.", "Army."], words: [" ", "Army."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Army.", "Army.", "Army."], words: ["Army.", "Army."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Army.", "Army.", "Army."], words: ["Army.", "Army."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Army.", "Army.", "Army."], words: ["Army.", "Army."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "These are my awards, Mother. From Army.", lines: ["Army.", "Army.", "Army."], words: ["Army.", "Army."], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "These are my awards, Mother. From Army.", paragraph: "I deceived you, mom.", lines: ["Army.", "Army.", "I deceived you, mom."], words: ["Army.", ""], - element: ["AXStaticText", "These are my awards, Mother. From Army."] }, + element: ["AXStaticText", + "These are my awards, Mother. From Army.", + "These are my awards, Mother. From Army."] }, { style: "I ", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["I", " "], - element: ["AXStaticText", "I "] }, - { style: "deceived you", + element: ["AXStaticText", "I ", "I "] }, + { style: "I ", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: [" ", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXStaticText", "I ", "I "] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", "deceived"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["deceived", " "], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: [" ", "you"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["you", "you"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["deceived you", "deceived you", "deceived you"], words: ["you", "you"], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: "deceived you", paragraph: "deceived you", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["you", ""], - element: ["AXTextField", "deceived you"] }, + element: ["AXTextField", "deceived you", "deceived you"] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: [",", " "], - element: ["AXStaticText", ", mom."] }, + element: ["AXStaticText", ", mom.", ", mom."] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: [", ", "mom."], - element: ["AXStaticText", ", mom."] }, + element: ["AXStaticText", ", mom.", ", mom."] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["mom.", "mom."], - element: ["AXStaticText", ", mom."] }, + element: ["AXStaticText", ", mom.", ", mom."] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["mom.", "mom."], - element: ["AXStaticText", ", mom."] }, + element: ["AXStaticText", ", mom.", ", mom."] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["mom.", "mom."], - element: ["AXStaticText", ", mom."] }, + element: ["AXStaticText", ", mom.", ", mom."] }, { style: ", mom.", paragraph: "I deceived you, mom.", lines: ["I deceived you, mom.", "I deceived you, mom.", "I deceived you, mom."], words: ["mom.", ""], - element: ["AXStaticText", ", mom."] }]; + element: ["AXStaticText", ", mom.", ", mom."] }]; diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/doc_tree.xhtml firefox-85.0+build1/accessible/tests/browser/mac/doc_tree.xhtml --- firefox-84.0.2+build1/accessible/tests/browser/mac/doc_tree.xhtml 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/doc_tree.xhtml 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru firefox-84.0.2+build1/accessible/tests/browser/mac/head.js firefox-85.0+build1/accessible/tests/browser/mac/head.js --- firefox-84.0.2+build1/accessible/tests/browser/mac/head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/mac/head.js 2021-01-18 19:58:08.000000000 +0000 @@ -28,13 +28,25 @@ } function waitForMacEventWithInfo(notificationType, filter) { + let filterFunc = (macIface, data) => { + if (!filter) { + return true; + } + + if (typeof filter == "function") { + return filter(macIface, data); + } + + return macIface.getAttributeValue("AXDOMIdentifier") == filter; + }; + return new Promise(resolve => { let eventObserver = { observe(subject, topic, data) { let macEvent = subject.QueryInterface(Ci.nsIAccessibleMacEvent); if ( data === notificationType && - (!filter || filter(macEvent.macIface, macEvent.data)) + filterFunc(macEvent.macIface, macEvent.data) ) { Services.obs.removeObserver(this, "accessible-mac-event"); resolve(macEvent); diff -Nru firefox-84.0.2+build1/accessible/tests/browser/shared-head.js firefox-85.0+build1/accessible/tests/browser/shared-head.js --- firefox-84.0.2+build1/accessible/tests/browser/shared-head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/browser/shared-head.js 2021-01-18 19:58:08.000000000 +0000 @@ -431,7 +431,11 @@ gIsRemoteIframe = options.remoteIframe; gIsIframe = options.iframe || gIsRemoteIframe; let url; - if (doc.endsWith("html") && !gIsIframe) { + if (options.chrome && doc.endsWith("html")) { + // Load with a chrome:// URL so this loads as a chrome document in the + // parent process. + url = `${CURRENT_DIR}${doc}`; + } else if (doc.endsWith("html") && !gIsIframe) { url = `${CURRENT_CONTENT_DIR}${doc}`; } else { url = snippetToURL(doc, options); @@ -445,10 +449,13 @@ } }); - const onContentDocLoad = waitForEvent( - EVENT_DOCUMENT_LOAD_COMPLETE, - DEFAULT_CONTENT_DOC_BODY_ID - ); + let onContentDocLoad; + if (!options.chrome) { + onContentDocLoad = waitForEvent( + EVENT_DOCUMENT_LOAD_COMPLETE, + DEFAULT_CONTENT_DOC_BODY_ID + ); + } let onIframeDocLoad; if (options.remoteIframe && !options.skipFissionDocLoad) { @@ -476,11 +483,29 @@ await SimpleTest.promiseFocus(browser); await loadContentScripts(browser, "Common.jsm"); - if (Services.appinfo.browserTabsRemoteAutostart) { + if (options.chrome) { + ok(!browser.isRemoteBrowser, "Not remote browser"); + } else if (Services.appinfo.browserTabsRemoteAutostart) { ok(browser.isRemoteBrowser, "Actually remote browser"); } - const { accessible: docAccessible } = await onContentDocLoad; + let docAccessible; + if (options.chrome) { + // Chrome documents don't fire DOCUMENT_LOAD_COMPLETE. Instead, wait + // until we can get the DocAccessible and it doesn't have the busy + // state. + await BrowserTestUtils.waitForCondition(() => { + docAccessible = getAccessible(browser.contentWindow.document); + if (!docAccessible) { + return false; + } + const state = {}; + docAccessible.getState(state, {}); + return !(state.value & STATE_BUSY); + }); + } else { + ({ accessible: docAccessible } = await onContentDocLoad); + } let iframeDocAccessible; if (gIsIframe) { if (!options.skipFissionDocLoad) { @@ -515,6 +540,12 @@ * - {Boolean} topLevel * Flag to run the test with content in the top level content process. * Default is true. + * - {Boolean} chrome + * Flag to run the test with content as a chrome document in the + * parent process. Default is false. This is only valid if url is a + * relative URL to a XUL document, not a markup snippet. topLevel + * should usually be set to false in this case, since XUL documents + * don't work in the content process. * - {Boolean} iframe * Flag to run the test with content wrapped in an iframe. Default is * false. @@ -533,11 +564,28 @@ * a set of attributes to be applied to a iframe content document body */ function addAccessibleTask(doc, task, options = {}) { - const { topLevel = true, iframe = false, remoteIframe = false } = options; + const { + topLevel = true, + chrome = false, + iframe = false, + remoteIframe = false, + } = options; if (topLevel) { add_task( accessibleTask(doc, task, { ...options, + chrome: false, + iframe: false, + remoteIframe: false, + }) + ); + } + + if (chrome) { + add_task( + accessibleTask(doc, task, { + ...options, + topLevel: false, iframe: false, remoteIframe: false, }) @@ -549,6 +597,7 @@ accessibleTask(doc, task, { ...options, topLevel: false, + chrome: false, remoteIframe: false, }) ); @@ -559,6 +608,7 @@ accessibleTask(doc, task, { ...options, topLevel: false, + chrome: false, iframe: false, }) ); diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/attributes/test_obj_css.xhtml firefox-85.0+build1/accessible/tests/mochitest/attributes/test_obj_css.xhtml --- firefox-84.0.2+build1/accessible/tests/mochitest/attributes/test_obj_css.xhtml 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/attributes/test_obj_css.xhtml 2021-01-18 19:58:08.000000000 +0000 @@ -22,9 +22,6 @@ // CSS display testCSSAttrs("display_mozbox"); testCSSAttrs("display_mozinlinebox"); - testCSSAttrs("display_mozgrid"); - testCSSAttrs("display_mozgridgroup"); - testCSSAttrs("display_mozgridline"); testCSSAttrs("display_mozdeck"); testCSSAttrs("display_mozpopup"); @@ -53,9 +50,6 @@ - - - diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/attributes/test_obj.html firefox-85.0+build1/accessible/tests/mochitest/attributes/test_obj.html --- firefox-84.0.2+build1/accessible/tests/mochitest/attributes/test_obj.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/attributes/test_obj.html 2021-01-18 19:58:08.000000000 +0000 @@ -72,6 +72,11 @@ testAttrs("live", {"live": "polite"}, true); testAttrs("live2", {"live": "polite"}, true); testAbsentAttrs("live3", {"live": ""}); + if (MAC) { + testAttrs("alert", {"live": "assertive"}, true); + } else { + testAbsentAttrs("alert", {"live": "assertive"}); + } testAttrs("log", {"live": "polite"}, true); testAttrs("logAssertive", {"live": "assertive"}, true); testAttrs("marquee", {"live": "off"}, true); @@ -82,6 +87,11 @@ // container-live object attribute testAttrs("liveChild", {"container-live": "polite"}, true); testAttrs("live2Child", {"container-live": "polite"}, true); + if (MAC) { + testAttrs("alertChild", {"container-live": "assertive"}, true); + } else { + testAbsentAttrs("alertChild", {"container-live": "assertive"}); + } testAttrs("logChild", {"container-live": "polite"}, true); testAttrs("logAssertiveChild", {"container-live": "assertive"}, true); testAttrs("marqueeChild", {"container-live": "off"}, true); @@ -234,6 +244,7 @@
excuse
me
excuse
me
excuse
+
excuse
me
excuse
me
excuse
me
diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/elm/a11y.ini firefox-85.0+build1/accessible/tests/mochitest/elm/a11y.ini --- firefox-84.0.2+build1/accessible/tests/mochitest/elm/a11y.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/elm/a11y.ini 2021-01-18 19:58:07.000000000 +0000 @@ -10,7 +10,6 @@ [test_listbox.xhtml] [test_MathMLSpec.html] [test_nsApplicationAcc.html] -[test_plugin.html] [test_canvas.html] [test_shadowroot.html] support-files = test_shadowroot_subframe.html diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_HTMLSpec.html firefox-85.0+build1/accessible/tests/mochitest/elm/test_HTMLSpec.html --- firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_HTMLSpec.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/elm/test_HTMLSpec.html 2021-01-18 19:58:08.000000000 +0000 @@ -461,20 +461,8 @@ // //////////////////////////////////////////////////////////////////////// // HTML:embed (windowless/windowed plugins and media) - if (WIN) { - obj = { - role: ROLE_EMBEDDED_OBJECT, - states: STATE_UNAVAILABLE, - }; - - testElm("embed_plugin_windowless", obj); - - obj = { - role: ROLE_EMBEDDED_OBJECT, - absentStates: STATE_UNAVAILABLE, - }; - testElm("embed_plugin_windowed", obj); - } + ok(!isAccessible("embed_plugin_windowless"), "(blocked) windowless plugin embed element is not accessible"); + ok(!isAccessible("embed_plugin_windowed"), "(blocked) windowed plugin embed element is not accessible"); obj = { role: ROLE_GRAPHIC, @@ -1133,20 +1121,8 @@ // //////////////////////////////////////////////////////////////////////// // HTML:object (windowless/windowed plugins and media) and HTML:param - if (WIN) { - obj = { - role: ROLE_EMBEDDED_OBJECT, - states: STATE_UNAVAILABLE, - children: [ ], // no child for HTML:param - }; - testElm("object_plugin_windowless", obj); - - obj = { - role: ROLE_EMBEDDED_OBJECT, - absentStates: STATE_UNAVAILABLE, - }; - testElm("object_plugin_windowed", obj); - } + ok(!isAccessible("object_plugin_windowless"), "(blocked) windowless plugin object element is not accessible"); + ok(!isAccessible("object_plugin_windowed"), "(blocked) windowed plugin object element is not accessible"); obj = { role: ROLE_GRAPHIC, @@ -1481,7 +1457,6 @@ SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); - setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_MathMLSpec.html firefox-85.0+build1/accessible/tests/mochitest/elm/test_MathMLSpec.html --- firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_MathMLSpec.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/elm/test_MathMLSpec.html 2021-01-18 19:58:08.000000000 +0000 @@ -351,7 +351,6 @@ SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); - setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_plugin.html firefox-85.0+build1/accessible/tests/mochitest/elm/test_plugin.html --- firefox-84.0.2+build1/accessible/tests/mochitest/elm/test_plugin.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/elm/test_plugin.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ - - - - Plugin tests - - - - - - - - - - - - - Bug 485270 - Bug 816856 - Bug 881636 -

- -
-  
- - - - foo - foo - - diff -Nru firefox-84.0.2+build1/accessible/tests/mochitest/focus/test_takeFocus.html firefox-85.0+build1/accessible/tests/mochitest/focus/test_takeFocus.html --- firefox-84.0.2+build1/accessible/tests/mochitest/focus/test_takeFocus.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/accessible/tests/mochitest/focus/test_takeFocus.html 2021-01-18 19:58:08.000000000 +0000 @@ -49,7 +49,6 @@ gQueue.push(new takeFocusInvoker("aria-link2")); gQueue.push(new takeFocusInvoker("link")); gQueue.push(new takeFocusInvoker("item2")); - gQueue.push(new takeFocusInvoker("plugin")); gQueue.push(new takeFocusInvoker(document)); gQueue.push(new takeFocusInvoker("lb_item2")); gQueue.push(new takeFocusInvoker(document)); @@ -60,13 +59,8 @@ gQueue.invoke(); // Will call SimpleTest.finish(); } - function waitForPlugin() { - window.setTimeout((isAccessible("plugin") ? doTest : waitForPlugin), 0); - } - SimpleTest.waitForExplicitFinish(); - setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED); - addA11yLoadEvent(waitForPlugin); + addA11yLoadEvent(doTest); @@ -83,11 +77,6 @@ Mozilla Bug 452710 - Mozilla Bug 646361 - - Mozilla Bug 706067 @@ -108,8 +97,6 @@
item3
- - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/favicons/browser_favicon_load.js firefox-85.0+build1/browser/base/content/test/favicons/browser_favicon_load.js --- firefox-84.0.2+build1/browser/base/content/test/favicons/browser_favicon_load.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/favicons/browser_favicon_load.js 2021-01-18 19:58:08.000000000 +0000 @@ -100,11 +100,9 @@ function waitOnFaviconLoaded(aFaviconURL) { return PlacesTestUtils.waitForNotification( - "onPageChanged", - (uri, attr, value, id) => - attr === Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON && - value === aFaviconURL, - "history" + "favicon-changed", + events => events.some(e => e.faviconUrl == aFaviconURL), + "places" ); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/forms/browser_selectpopup.js firefox-85.0+build1/browser/base/content/test/forms/browser_selectpopup.js --- firefox-84.0.2+build1/browser/base/content/test/forms/browser_selectpopup.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/forms/browser_selectpopup.js 2021-01-18 19:58:08.000000000 +0000 @@ -904,7 +904,7 @@ let browserLoadedPromise = BrowserTestUtils.browserLoaded( newWin.gBrowser.selectedBrowser ); - await BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, pageUrl); + BrowserTestUtils.loadURI(newWin.gBrowser.selectedBrowser, pageUrl); await browserLoadedPromise; newWin.gBrowser.selectedBrowser.focus(); diff -Nru firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser_bug1620341.js firefox-85.0+build1/browser/base/content/test/fullscreen/browser_bug1620341.js --- firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser_bug1620341.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/fullscreen/browser_bug1620341.js 2021-01-18 19:58:08.000000000 +0000 @@ -43,9 +43,6 @@ frameDoc.addEventListener(message, handler); }); - // In fission, we may not have docShell active automatically, - // Force docShell active manually - content.docShell.isActive = true; frameDoc.getElementById("request").click(); await waitForFullscreen; }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js firefox-85.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js --- firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/fullscreen/browser_fullscreen_cross_origin.js 2021-01-18 19:58:07.000000000 +0000 @@ -31,9 +31,6 @@ } frameDoc.addEventListener(message, handler); }); - // In fission, we may not have docShell active automatically, - // Force docShell active manually - content.docShell.isActive = true; frameDoc.getElementById("request").click(); await waitForFullscreen; }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser.ini firefox-85.0+build1/browser/base/content/test/fullscreen/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/fullscreen/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/fullscreen/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -15,6 +15,6 @@ [browser_fullscreen_window_open.js] skip-if = debug && os == 'mac' # Bug 1568570 [browser_fullscreen_window_focus.js] -skip-if = os == 'mac' || (debug && fission && os == "linux" && os_version == "18.04") # Bug 1568570, 1618386 +skip-if = os == 'mac' # Bug 1568570 [browser_fullscreen_api_fission.js] support-files = fullscreen.html FullscreenFrame.jsm diff -Nru firefox-84.0.2+build1/browser/base/content/test/fullscreen/FullscreenFrame.jsm firefox-85.0+build1/browser/base/content/test/fullscreen/FullscreenFrame.jsm --- firefox-84.0.2+build1/browser/base/content/test/fullscreen/FullscreenFrame.jsm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/fullscreen/FullscreenFrame.jsm 2021-01-18 19:58:08.000000000 +0000 @@ -45,7 +45,7 @@ case "ExitFullscreen": return this.contentWindow.document.exitFullscreen(); case "RequestFullscreen": - this.docShell.isActive = true; + this.browsingContext.isActive = true; return Promise.all([this.changed(), this.requestFullscreen()]); case "CreateChild": let child = msg.data; diff -Nru firefox-84.0.2+build1/browser/base/content/test/fullscreen/head.js firefox-85.0+build1/browser/base/content/test/fullscreen/head.js --- firefox-84.0.2+build1/browser/base/content/test/fullscreen/head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/fullscreen/head.js 2021-01-18 19:58:08.000000000 +0000 @@ -38,7 +38,7 @@ SpecialPowers.spawn(browser, [fullScreenState], async state => { // Wait for document focus before requesting full-screen await ContentTaskUtils.waitForCondition( - () => docShell.isActive && content.document.hasFocus(), + () => content.browsingContext.isActive && content.document.hasFocus(), "Waiting for document focus" ); if (state) { diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_addCertException.js firefox-85.0+build1/browser/base/content/test/general/browser_addCertException.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_addCertException.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_addCertException.js 2021-01-18 19:58:08.000000000 +0000 @@ -53,17 +53,17 @@ .getPropertyValue("background-image"); is( identityIconImage, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "Using expected icon image in the identity block" ); is( securityViewBG, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "Using expected icon image in the Control Center main view" ); is( securityContentBG, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "Using expected icon image in the Control Center subview" ); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_alltabslistener.js firefox-85.0+build1/browser/base/content/test/general/browser_alltabslistener.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_alltabslistener.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_alltabslistener.js 2021-01-18 19:58:08.000000000 +0000 @@ -25,17 +25,7 @@ info( "FrontProgress (" + url + "): " + state + " 0x" + aStateFlags.toString(16) ); - Assert.less( - gFrontNotificationsPos, - gFrontNotifications.length, - "Got an expected notification for the front notifications listener" - ); - is( - state, - gFrontNotifications[gFrontNotificationsPos], - "Got a notification for the front notifications listener" - ); - gFrontNotificationsPos++; + assertCorrectBrowserAndEventOrderForFront(state); }, onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) { @@ -45,17 +35,7 @@ } var state = "onLocationChange"; info("FrontProgress: " + state + " " + aLocationURI.spec); - Assert.less( - gFrontNotificationsPos, - gFrontNotifications.length, - "Got an expected notification for the front notifications listener" - ); - is( - state, - gFrontNotifications[gFrontNotificationsPos], - "Got a notification for the front notifications listener" - ); - gFrontNotificationsPos++; + assertCorrectBrowserAndEventOrderForFront(state); }, onSecurityChange(aWebProgress, aRequest, aState) { @@ -65,20 +45,24 @@ } var state = "onSecurityChange"; info("FrontProgress (" + url + "): " + state + " 0x" + aState.toString(16)); - Assert.less( - gFrontNotificationsPos, - gFrontNotifications.length, - "Got an expected notification for the front notifications listener" - ); - is( - state, - gFrontNotifications[gFrontNotificationsPos], - "Got a notification for the front notifications listener" - ); - gFrontNotificationsPos++; + assertCorrectBrowserAndEventOrderForFront(state); }, }; +function assertCorrectBrowserAndEventOrderForFront(aEventName) { + Assert.less( + gFrontNotificationsPos, + gFrontNotifications.length, + "Got an expected notification for the front notifications listener" + ); + is( + aEventName, + gFrontNotifications[gFrontNotificationsPos], + "Got a notification for the front notifications listener" + ); + gFrontNotificationsPos++; +} + var gAllProgressListener = { onStateChange(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { var url = getOriginalURL(aRequest); @@ -90,19 +74,11 @@ info( "AllProgress (" + url + "): " + state + " 0x" + aStateFlags.toString(16) ); - ok( - aBrowser == gTestBrowser, - state + " notification came from the correct browser" - ); - Assert.less( - gAllNotificationsPos, - gAllNotifications.length, - "Got an expected notification for the all notifications listener" - ); - is( + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); + assertReceivedFlags( state, gAllNotifications[gAllNotificationsPos], - "Got a notification for the all notifications listener" + aStateFlags ); gAllNotificationsPos++; @@ -129,19 +105,11 @@ } var state = "onLocationChange"; info("AllProgress: " + state + " " + aLocationURI.spec); - ok( - aBrowser == gTestBrowser, - state + " notification came from the correct browser" - ); - Assert.less( - gAllNotificationsPos, - gAllNotifications.length, - "Got an expected notification for the all notifications listener" - ); - is( - state, + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); + assertReceivedFlags( + "onLocationChange", gAllNotifications[gAllNotificationsPos], - "Got a notification for the all notifications listener" + aFlags ); gAllNotificationsPos++; }, @@ -154,15 +122,7 @@ } var state = "onSecurityChange"; info("AllProgress (" + url + "): " + state + " 0x" + aState.toString(16)); - ok( - aBrowser == gTestBrowser, - state + " notification came from the correct browser" - ); - Assert.less( - gAllNotificationsPos, - gAllNotifications.length, - "Got an expected notification for the all notifications listener" - ); + assertCorrectBrowserAndEventOrderForAll(state, aBrowser); is( state, gAllNotifications[gAllNotificationsPos], @@ -172,6 +132,35 @@ }, }; +function assertCorrectBrowserAndEventOrderForAll(aState, aBrowser) { + ok( + aBrowser == gTestBrowser, + aState + " notification came from the correct browser" + ); + Assert.less( + gAllNotificationsPos, + gAllNotifications.length, + "Got an expected notification for the all notifications listener" + ); +} + +function assertReceivedFlags(aState, aObjOrEvent, aFlags) { + if (aObjOrEvent !== null && typeof aObjOrEvent === "object") { + is( + aState, + aObjOrEvent.state, + "Got a notification for the all notifications listener" + ); + is(aFlags, aFlags & aObjOrEvent.flags, `Got correct flags for ${aState}`); + } else { + is( + aState, + aObjOrEvent, + "Got a notification for the all notifications listener" + ); + } +} + var gFrontNotifications, gAllNotifications, gFrontNotificationsPos, @@ -187,23 +176,6 @@ "http://mochi.test:8888/browser/browser/base/content/test/general/dummy_page.html"; var gNextTest; -function setExpectationForCrossDomainFrontBrowserLoad() { - // In fission, we swap remoteness for this load, and we'll get sent a - // notification to ensure the security state shown by the browser remains - // correct after the remoteness change - we need to account for that: - if (gFissionBrowser) { - gFrontNotifications = [ - "onStateChange", - "onSecurityChange", - "onLocationChange", - "onSecurityChange", - "onStateChange", - ]; - } else { - gFrontNotifications = gAllNotifications; - } -} - async function test() { waitForExplicitFinish(); @@ -250,13 +222,13 @@ gBrowser.addProgressListener(gFrontProgressListener); gBrowser.addTabsProgressListener(gAllProgressListener); - setExpectationForCrossDomainFrontBrowserLoad(); + gFrontNotifications = gAllNotifications; runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest2); } function startTest2() { info("\nTest 2"); - setExpectationForCrossDomainFrontBrowserLoad(); + gFrontNotifications = gAllNotifications; runTest(gForegroundBrowser, "https://example.com" + gTestPage, startTest3); } @@ -285,13 +257,63 @@ gBrowser.selectedTab = gForegroundTab; gBrowser.addProgressListener(gFrontProgressListener); - setExpectationForCrossDomainFrontBrowserLoad(); + gFrontNotifications = gAllNotifications; runTest(gForegroundBrowser, "http://example.org" + gTestPage, startTest6); } function startTest6() { info("\nTest 6"); gFrontNotifications = []; + runTest(gBackgroundBrowser, "http://example.org" + gTestPage, startTest7); +} + +// Navigate from remote to non-remote +function startTest7() { + info("\nTest 7"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + { + state: "onLocationChange", + flags: Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT, + }, // dummy onLocationChange event + "onStateChange", + ]; + runTest(gBackgroundBrowser, "about:preferences", startTest8); +} + +// Navigate from non-remote to non-remote +function startTest8() { + info("\nTest 8"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + { + state: "onStateChange", + flags: + Ci.nsIWebProgressListener.STATE_IS_REDIRECTED_DOCUMENT | + Ci.nsIWebProgressListener.STATE_IS_REQUEST | + Ci.nsIWebProgressListener.STATE_START, + }, + "onLocationChange", + "onSecurityChange", + "onStateChange", + ]; + runTest(gBackgroundBrowser, "about:config", startTest9); +} + +// Navigate from non-remote to remote +function startTest9() { + info("\nTest 9"); + gFrontNotifications = []; + gAllNotifications = [ + "onStateChange", + "onLocationChange", + "onSecurityChange", + "onStateChange", + ]; runTest(gBackgroundBrowser, "http://example.org" + gTestPage, finishTest); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_bug495058.js firefox-85.0+build1/browser/base/content/test/general/browser_bug495058.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_bug495058.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_bug495058.js 2021-01-18 19:58:08.000000000 +0000 @@ -8,7 +8,8 @@ add_task(async function() { for (let uri of URIS) { let tab = BrowserTestUtils.addTab(gBrowser); - await BrowserTestUtils.loadURI(tab.linkedBrowser, uri); + BrowserTestUtils.loadURI(tab.linkedBrowser, uri); + await BrowserTestUtils.browserLoaded(tab.linkedBrowser); let win = gBrowser.replaceTabWithWindow(tab); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_bug724239.js firefox-85.0+build1/browser/base/content/test/general/browser_bug724239.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_bug724239.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_bug724239.js 2021-01-18 19:58:08.000000000 +0000 @@ -22,11 +22,11 @@ async function(browser) { // Can't load it directly because that'll use a preloaded tab if present. let stopped = BrowserTestUtils.browserStopped(browser, "about:newtab"); - await BrowserTestUtils.loadURI(browser, "about:newtab"); + BrowserTestUtils.loadURI(browser, "about:newtab"); await stopped; stopped = BrowserTestUtils.browserStopped(browser, "http://example.com/"); - await BrowserTestUtils.loadURI(browser, "http://example.com/"); + BrowserTestUtils.loadURI(browser, "http://example.com/"); await stopped; // This makes sure the parent process has the most up-to-date notion diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_bug763468_perwindowpb.js firefox-85.0+build1/browser/base/content/test/general/browser_bug763468_perwindowpb.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_bug763468_perwindowpb.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_bug763468_perwindowpb.js 2021-01-18 19:58:08.000000000 +0000 @@ -45,18 +45,13 @@ async function openNewTab(aWindow, aExpectedURL) { // Open a new tab aWindow.BrowserOpenTab(); - let browser = aWindow.gBrowser.selectedBrowser; - let loadPromise = BrowserTestUtils.browserLoaded( - browser, - false, - aExpectedURL - ); - let alreadyLoaded = await ContentTask.spawn(browser, aExpectedURL, url => { - let doc = content.document; - return doc && doc.readyState === "complete" && doc.location.href == url; - }); - if (!alreadyLoaded) { - await loadPromise; + + // We're already loaded. + if (browser.currentURI.spec === aExpectedURL) { + return; } + + // Wait for any location change. + await BrowserTestUtils.waitForLocationChange(aWindow.gBrowser); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_bug767836_perwindowpb.js firefox-85.0+build1/browser/base/content/test/general/browser_bug767836_perwindowpb.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_bug767836_perwindowpb.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_bug767836_perwindowpb.js 2021-01-18 19:58:08.000000000 +0000 @@ -64,18 +64,13 @@ async function openNewTab(aWindow, aExpectedURL) { // Open a new tab aWindow.BrowserOpenTab(); - let browser = aWindow.gBrowser.selectedBrowser; - let loadPromise = BrowserTestUtils.browserLoaded( - browser, - false, - aExpectedURL - ); - let alreadyLoaded = await ContentTask.spawn(browser, aExpectedURL, url => { - let doc = content.document; - return doc && doc.readyState === "complete" && doc.location.href == url; - }); - if (!alreadyLoaded) { - await loadPromise; + + // We're already loaded. + if (browser.currentURI.spec === aExpectedURL) { + return; } + + // Wait for any location change. + await BrowserTestUtils.waitForLocationChange(aWindow.gBrowser); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_ctrlTab.js firefox-85.0+build1/browser/base/content/test/general/browser_ctrlTab.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_ctrlTab.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_ctrlTab.js 2021-01-18 19:58:07.000000000 +0000 @@ -211,6 +211,17 @@ ? "back to the previously selected tab" : normalized + " tabs back in most-recently-selected order"; + // Add keyup listener to all content documents. + await Promise.all( + gBrowser.tabs.map(tab => + SpecialPowers.spawn(tab.linkedBrowser, [], () => { + content.window.addEventListener("keyup", () => { + content.window._ctrlTabTestKeyupHappend = true; + }); + }) + ) + ); + for (let i = 0; i < tabTimes; i++) { await pressCtrlTab(); @@ -253,5 +264,19 @@ " goes " + where ); + + const keyupEvents = await Promise.all( + gBrowser.tabs.map(tab => + SpecialPowers.spawn( + tab.linkedBrowser, + [], + () => !!content.window._ctrlTabTestKeyupHappend + ) + ) + ); + ok( + keyupEvents.every(isKeyupHappned => !isKeyupHappned), + "Content document doesn't capture Keyup event during cycling tabs" + ); } }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_datachoices_notification.js firefox-85.0+build1/browser/base/content/test/general/browser_datachoices_notification.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_datachoices_notification.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_datachoices_notification.js 2021-01-18 19:58:07.000000000 +0000 @@ -143,12 +143,7 @@ Preferences.reset(PREF_ACCEPTED_POLICY_DATE); } -add_task(async function test_single_window() { - clearAcceptedPolicy(); - - // Close all the notifications, then try to trigger the data choices infobar. - await closeAllNotifications(); - +function assertCoherentInitialState() { // Make sure that we have a coherent initial state. Assert.equal( Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), @@ -164,6 +159,15 @@ !TelemetryReportingPolicy.testIsUserNotified(), "User not notified about datareporting policy." ); +} + +add_task(async function test_single_window() { + clearAcceptedPolicy(); + + // Close all the notifications, then try to trigger the data choices infobar. + await closeAllNotifications(); + + assertCoherentInitialState(); let alertShownPromise = promiseWaitForAlertActive(gNotificationBox); Assert.ok( @@ -219,9 +223,11 @@ add_task(async function test_multiple_windows() { clearAcceptedPolicy(); + assertCoherentInitialState(); // bug 1571932 // Close all the notifications, then try to trigger the data choices infobar. await closeAllNotifications(); + assertCoherentInitialState(); // bug 1571932 // Ensure we see the notification on all windows and that action on one window // results in dismiss on every window. @@ -232,21 +238,7 @@ "2nd window has a global notification box." ); - // Make sure that we have a coherent initial state. - Assert.equal( - Preferences.get(PREF_ACCEPTED_POLICY_VERSION, 0), - 0, - "No version should be set on init." - ); - Assert.equal( - Preferences.get(PREF_ACCEPTED_POLICY_DATE, 0), - 0, - "No date should be set on init." - ); - Assert.ok( - !TelemetryReportingPolicy.testIsUserNotified(), - "User not notified about datareporting policy." - ); + assertCoherentInitialState(); let showAlertPromises = [ promiseWaitForAlertActive(gNotificationBox), diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js firefox-85.0+build1/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js 2021-01-18 19:58:08.000000000 +0000 @@ -37,7 +37,7 @@ function waitForDocActivated(aBrowser) { return SpecialPowers.spawn(aBrowser, [], () => { return ContentTaskUtils.waitForCondition( - () => docShell.isActive && content.document.hasFocus() + () => content.browsingContext.isActive && content.document.hasFocus() ); }); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser.ini firefox-85.0+build1/browser/base/content/test/general/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/general/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -54,7 +54,6 @@ [browser_addKeywordSearch.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_alltabslistener.js] -skip-if = fission #Bug 1661833 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_backButtonFitts.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. @@ -231,7 +230,6 @@ [browser_homeDrop.js] # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_invalid_uri_back_forward_manipulation.js] -skip-if = fission # Bug 1667386 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD. [browser_lastAccessedTab.js] skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405) diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_newTabDrop.js firefox-85.0+build1/browser/base/content/test/general/browser_newTabDrop.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_newTabDrop.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_newTabDrop.js 2021-01-18 19:58:08.000000000 +0000 @@ -26,20 +26,20 @@ // New Tab Button opens any link. add_task(async function single_url() { - await dropText("mochi.test/first", ["http://www.mochi.test/first"]); + await dropText("mochi.test/first", ["https://www.mochi.test/first"]); }); add_task(async function single_url2() { - await dropText("mochi.test/second", ["http://www.mochi.test/second"]); + await dropText("mochi.test/second", ["https://www.mochi.test/second"]); }); add_task(async function single_url3() { - await dropText("mochi.test/third", ["http://www.mochi.test/third"]); + await dropText("mochi.test/third", ["https://www.mochi.test/third"]); }); // Single text/plain item, with multiple links. add_task(async function multiple_urls() { await dropText("mochi.test/1\nmochi.test/2", [ - "http://www.mochi.test/1", - "http://www.mochi.test/2", + "https://www.mochi.test/1", + "https://www.mochi.test/2", ]); }); @@ -51,9 +51,9 @@ [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], ], [ - "http://www.mochi.test/5", - "http://www.mochi.test/6", - "http://www.mochi.test/7", + "https://www.mochi.test/5", + "https://www.mochi.test/6", + "https://www.mochi.test/7", ] ); }); @@ -70,7 +70,7 @@ }, ], ], - ["http://www.mochi.test/8", "http://www.mochi.test/9"] + ["https://www.mochi.test/8", "https://www.mochi.test/9"] ); }); @@ -83,7 +83,7 @@ { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, ], ], - ["http://www.mochi.test/11"] + ["https://www.mochi.test/11"] ); }); @@ -94,11 +94,11 @@ urls.push("mochi.test/multi" + i); } await dropText(urls.join("\n"), [ - "http://www.mochi.test/multi0", - "http://www.mochi.test/multi1", - "http://www.mochi.test/multi2", - "http://www.mochi.test/multi3", - "http://www.mochi.test/multi4", + "https://www.mochi.test/multi0", + "https://www.mochi.test/multi1", + "https://www.mochi.test/multi2", + "https://www.mochi.test/multi3", + "https://www.mochi.test/multi4", ]); }); add_task(async function multiple_tabs_over_max_accept() { @@ -111,11 +111,11 @@ urls.push("mochi.test/accept" + i); } await dropText(urls.join("\n"), [ - "http://www.mochi.test/accept0", - "http://www.mochi.test/accept1", - "http://www.mochi.test/accept2", - "http://www.mochi.test/accept3", - "http://www.mochi.test/accept4", + "https://www.mochi.test/accept0", + "https://www.mochi.test/accept1", + "https://www.mochi.test/accept2", + "https://www.mochi.test/accept3", + "https://www.mochi.test/accept4", ]); await confirmPromise; @@ -151,10 +151,10 @@ non url2 `, [ - "http://www.mochi.test/urls0", - "http://www.mochi.test/urls1", - "http://www.mochi.test/urls2", - "http://www.mochi.test/urls3", + "https://www.mochi.test/urls0", + "https://www.mochi.test/urls1", + "https://www.mochi.test/urls2", + "https://www.mochi.test/urls3", ] ); }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_newWindowDrop.js firefox-85.0+build1/browser/base/content/test/general/browser_newWindowDrop.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_newWindowDrop.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_newWindowDrop.js 2021-01-18 19:58:08.000000000 +0000 @@ -38,7 +38,7 @@ // New Window Button opens any link. add_task(async function single_url() { - await dropText("mochi.test/first", ["http://www.mochi.test/first"]); + await dropText("mochi.test/first", ["https://www.mochi.test/first"]); }); add_task(async function single_javascript() { await dropText("javascript:'bad'", ["about:blank"]); @@ -47,31 +47,31 @@ await dropText("jAvascript:'bad'", ["about:blank"]); }); add_task(async function single_url2() { - await dropText("mochi.test/second", ["http://www.mochi.test/second"]); + await dropText("mochi.test/second", ["https://www.mochi.test/second"]); }); add_task(async function single_data_url() { await dropText("data:text/html,bad", ["data:text/html,bad"]); }); add_task(async function single_url3() { - await dropText("mochi.test/third", ["http://www.mochi.test/third"]); + await dropText("mochi.test/third", ["https://www.mochi.test/third"]); }); // Single text/plain item, with multiple links. add_task(async function multiple_urls() { await dropText("mochi.test/1\nmochi.test/2", [ - "http://www.mochi.test/1", - "http://www.mochi.test/2", + "https://www.mochi.test/1", + "https://www.mochi.test/2", ]); }); add_task(async function multiple_urls_javascript() { await dropText("javascript:'bad1'\nmochi.test/3", [ "about:blank", - "http://www.mochi.test/3", + "https://www.mochi.test/3", ]); }); add_task(async function multiple_urls_data() { await dropText("mochi.test/4\ndata:text/html,bad1", [ - "http://www.mochi.test/4", + "https://www.mochi.test/4", "data:text/html,bad1", ]); }); @@ -84,9 +84,9 @@ [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], ], [ - "http://www.mochi.test/5", - "http://www.mochi.test/6", - "http://www.mochi.test/7", + "https://www.mochi.test/5", + "https://www.mochi.test/6", + "https://www.mochi.test/7", ] ); }); @@ -103,7 +103,7 @@ }, ], ], - ["http://www.mochi.test/8", "http://www.mochi.test/9"] + ["https://www.mochi.test/8", "https://www.mochi.test/9"] ); }); @@ -116,7 +116,7 @@ { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, ], ], - ["http://www.mochi.test/11"] + ["https://www.mochi.test/11"] ); }); @@ -127,11 +127,11 @@ urls.push("mochi.test/multi" + i); } await dropText(urls.join("\n"), [ - "http://www.mochi.test/multi0", - "http://www.mochi.test/multi1", - "http://www.mochi.test/multi2", - "http://www.mochi.test/multi3", - "http://www.mochi.test/multi4", + "https://www.mochi.test/multi0", + "https://www.mochi.test/multi1", + "https://www.mochi.test/multi2", + "https://www.mochi.test/multi3", + "https://www.mochi.test/multi4", ]); }); add_task(async function multiple_tabs_over_max_accept() { @@ -146,11 +146,11 @@ await dropText( urls.join("\n"), [ - "http://www.mochi.test/accept0", - "http://www.mochi.test/accept1", - "http://www.mochi.test/accept2", - "http://www.mochi.test/accept3", - "http://www.mochi.test/accept4", + "https://www.mochi.test/accept0", + "https://www.mochi.test/accept1", + "https://www.mochi.test/accept2", + "https://www.mochi.test/accept3", + "https://www.mochi.test/accept4", ], true ); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_page_style_menu.js firefox-85.0+build1/browser/base/content/test/general/browser_page_style_menu.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_page_style_menu.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_page_style_menu.js 2021-01-18 19:58:07.000000000 +0000 @@ -23,6 +23,8 @@ "http://example.com" ); +const kStyleSheetsInPageStyleSample = 18; + /* * Test that the right stylesheets do (and others don't) show up * in the page style menu. @@ -34,8 +36,8 @@ false ); let browser = tab.linkedBrowser; - await BrowserTestUtils.loadURI(browser, WEB_ROOT + "page_style_sample.html"); - await promiseStylesheetsLoaded(tab, 17); + BrowserTestUtils.loadURI(browser, WEB_ROOT + "page_style_sample.html"); + await promiseStylesheetsLoaded(tab, kStyleSheetsInPageStyleSample); let menuitems = fillPopupAndGetItems(); let items = menuitems.map(el => ({ @@ -96,7 +98,7 @@ let disableStyles = document.getElementById("menu_pageStyleNoStyle"); let defaultStyles = document.getElementById("menu_pageStylePersistentOnly"); - let otherStyles = menuitems[menuitems.length - 1]; + let otherStyles = menuitems[0].parentNode.querySelector("[label='28']"); // Assert that the menu works as expected. disableStyles.click(); @@ -163,8 +165,12 @@ new FileUtils.File(getTestFilePath("page_style_sample.html")) ).spec; await BrowserTestUtils.withNewTab(FILE_PAGE, async function(browser) { - await promiseStylesheetsLoaded(browser, 17); + await promiseStylesheetsLoaded(browser, kStyleSheetsInPageStyleSample); let menuitems = fillPopupAndGetItems(); - is(menuitems.length, 17, "Should have 17 items even for file: URI."); + is( + menuitems.length, + kStyleSheetsInPageStyleSample, + "Should have the right amount of items even for file: URI." + ); }); }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_page_style_menu_update.js firefox-85.0+build1/browser/base/content/test/general/browser_page_style_menu_update.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_page_style_menu_update.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_page_style_menu_update.js 2021-01-18 19:58:07.000000000 +0000 @@ -14,8 +14,8 @@ false ); let browser = tab.linkedBrowser; - await BrowserTestUtils.loadURI(browser, PAGE); - await promiseStylesheetsLoaded(tab, 17); + BrowserTestUtils.loadURI(browser, PAGE); + await promiseStylesheetsLoaded(tab, 18); let menupopup = document.getElementById("pageStyleMenu").menupopup; gPageStyleMenu.fillPopup(menupopup); diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_tabDrop.js firefox-85.0+build1/browser/base/content/test/general/browser_tabDrop.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_tabDrop.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_tabDrop.js 2021-01-18 19:58:08.000000000 +0000 @@ -1,3 +1,6 @@ +// TODO (Bug 1680996): Investigate why this test takes a long time. +requestLongerTimeout(2); + const ANY_URL = undefined; registerCleanupFunction(async function cleanup() { @@ -22,7 +25,7 @@ }); add_task(async function single_url() { - await dropText("mochi.test/first", ["http://www.mochi.test/first"]); + await dropText("mochi.test/first", ["https://www.mochi.test/first"]); }); add_task(async function single_javascript() { await dropText("javascript:'bad'", []); @@ -34,20 +37,20 @@ await dropText("search this", [ANY_URL]); }); add_task(async function single_url2() { - await dropText("mochi.test/second", ["http://www.mochi.test/second"]); + await dropText("mochi.test/second", ["https://www.mochi.test/second"]); }); add_task(async function single_data_url() { await dropText("data:text/html,bad", []); }); add_task(async function single_url3() { - await dropText("mochi.test/third", ["http://www.mochi.test/third"]); + await dropText("mochi.test/third", ["https://www.mochi.test/third"]); }); // Single text/plain item, with multiple links. add_task(async function multiple_urls() { await dropText("mochi.test/1\nmochi.test/2", [ - "http://www.mochi.test/1", - "http://www.mochi.test/2", + "https://www.mochi.test/1", + "https://www.mochi.test/2", ]); }); add_task(async function multiple_urls_javascript() { @@ -65,9 +68,9 @@ [{ type: "text/plain", data: "mochi.test/6\nmochi.test/7" }], ], [ - "http://www.mochi.test/5", - "http://www.mochi.test/6", - "http://www.mochi.test/7", + "https://www.mochi.test/5", + "https://www.mochi.test/6", + "https://www.mochi.test/7", ] ); }); @@ -84,7 +87,7 @@ }, ], ], - ["http://www.mochi.test/8", "http://www.mochi.test/9"] + ["https://www.mochi.test/8", "https://www.mochi.test/9"] ); }); @@ -97,7 +100,7 @@ { type: "text/x-moz-url", data: "mochi.test/11\nTITLE11" }, ], ], - ["http://www.mochi.test/11"] + ["https://www.mochi.test/11"] ); }); @@ -108,11 +111,11 @@ urls.push("mochi.test/multi" + i); } await dropText(urls.join("\n"), [ - "http://www.mochi.test/multi0", - "http://www.mochi.test/multi1", - "http://www.mochi.test/multi2", - "http://www.mochi.test/multi3", - "http://www.mochi.test/multi4", + "https://www.mochi.test/multi0", + "https://www.mochi.test/multi1", + "https://www.mochi.test/multi2", + "https://www.mochi.test/multi3", + "https://www.mochi.test/multi4", ]); }); add_task(async function multiple_tabs_over_max_accept() { @@ -125,11 +128,11 @@ urls.push("mochi.test/accept" + i); } await dropText(urls.join("\n"), [ - "http://www.mochi.test/accept0", - "http://www.mochi.test/accept1", - "http://www.mochi.test/accept2", - "http://www.mochi.test/accept3", - "http://www.mochi.test/accept4", + "https://www.mochi.test/accept0", + "https://www.mochi.test/accept1", + "https://www.mochi.test/accept2", + "https://www.mochi.test/accept3", + "https://www.mochi.test/accept4", ]); await confirmPromise; diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_tabs_isActive.js firefox-85.0+build1/browser/base/content/test/general/browser_tabs_isActive.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_tabs_isActive.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_tabs_isActive.js 2021-01-18 19:58:08.000000000 +0000 @@ -24,7 +24,11 @@ } function getChildTabState(aTab) { - return ContentTask.spawn(aTab.linkedBrowser, null, () => docShell.isActive); + return ContentTask.spawn( + aTab.linkedBrowser, + null, + () => content.browsingContext.isActive + ); } function checkState(parentSide, childSide, value, message) { diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/browser_windowactivation.js firefox-85.0+build1/browser/base/content/test/general/browser_windowactivation.js --- firefox-84.0.2+build1/browser/base/content/test/general/browser_windowactivation.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/browser_windowactivation.js 2021-01-18 19:58:08.000000000 +0000 @@ -4,98 +4,110 @@ /* eslint-env mozilla/frame-script */ -const testPage = getRootDirectory(gTestPath) + "file_window_activation.html"; -const testPage2 = getRootDirectory(gTestPath) + "file_window_activation2.html"; +const testPageChrome = + getRootDirectory(gTestPath) + "file_window_activation.html"; +const testPageHttp = testPageChrome.replace( + "chrome://mochitests/content", + "http://example.com" +); +const testPageWindow = + getRootDirectory(gTestPath) + "file_window_activation2.html"; add_task(async function reallyRunTests() { - let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage); - let browser1 = tab1.linkedBrowser; + let chromeTab1 = await BrowserTestUtils.openNewForegroundTab( + gBrowser, + testPageChrome + ); + let chromeBrowser1 = chromeTab1.linkedBrowser; - // This can't use openNewForegroundTab because if we focus tab2 now, we + // This can't use openNewForegroundTab because if we focus chromeTab2 now, we // won't send a focus event during test 6, further down in this file. - let tab2 = BrowserTestUtils.addTab(gBrowser, testPage); - let browser2 = tab2.linkedBrowser; - await BrowserTestUtils.browserLoaded(browser2); + let chromeTab2 = BrowserTestUtils.addTab(gBrowser, testPageChrome); + let chromeBrowser2 = chromeTab2.linkedBrowser; + await BrowserTestUtils.browserLoaded(chromeBrowser2); + + let httpTab = BrowserTestUtils.addTab(gBrowser, testPageHttp); + let httpBrowser = httpTab.linkedBrowser; + await BrowserTestUtils.browserLoaded(httpBrowser); function failTest() { ok(false, "Test received unexpected activate/deactivate event"); } - // The content should not receive activate or deactivate events. If it - // does, fail the test. - BrowserTestUtils.waitForContentEvent(browser1, "activate", true).then( - failTest - ); - BrowserTestUtils.waitForContentEvent(browser2, "activate", true).then( - failTest - ); - BrowserTestUtils.waitForContentEvent(browser1, "deactivate", true).then( - failTest - ); - BrowserTestUtils.waitForContentEvent(browser2, "deactivate", true).then( - failTest - ); + // chrome:// url tabs should not receive "activate" or "deactivate" events + // as they should be sent to the top-level window in the parent process. + for (let b of [chromeBrowser1, chromeBrowser2]) { + BrowserTestUtils.waitForContentEvent(b, "activate", true).then(failTest); + BrowserTestUtils.waitForContentEvent(b, "deactivate", true).then(failTest); + } gURLBar.focus(); - gBrowser.selectedTab = tab1; + gBrowser.selectedTab = chromeTab1; - // The test performs four checks, using -moz-window-inactive on two child tabs. + // The test performs four checks, using -moz-window-inactive on three child + // tabs (2 loading chrome:// urls and one loading an http:// url). // First, the initial state should be transparent. The second check is done // while another window is focused. The third check is done after that window // is closed and the main window focused again. The fourth check is done after // switching to the second tab. // Step 1 - check the initial state - let colorBrowser1 = await getBackgroundColor(browser1, true); - let colorBrowser2 = await getBackgroundColor(browser2, true); - is(colorBrowser1, "rgba(0, 0, 0, 0)", "first window initial"); - is(colorBrowser2, "rgba(0, 0, 0, 0)", "second window initial"); + let colorChromeBrowser1 = await getBackgroundColor(chromeBrowser1, true); + let colorChromeBrowser2 = await getBackgroundColor(chromeBrowser2, true); + let colorHttpBrowser = await getBackgroundColor(httpBrowser, true); + is(colorChromeBrowser1, "rgba(0, 0, 0, 0)", "first tab initial"); + is(colorChromeBrowser2, "rgba(0, 0, 0, 0)", "second tab initial"); + is(colorHttpBrowser, "rgba(0, 0, 0, 0)", "third tab initial"); // Step 2 - open and focus another window - let otherWindow = window.open(testPage2, "", "chrome"); + let otherWindow = window.open(testPageWindow, "", "chrome"); await SimpleTest.promiseFocus(otherWindow); - colorBrowser1 = await getBackgroundColor(browser1, true); - colorBrowser2 = await getBackgroundColor(browser2, true); - is(colorBrowser1, "rgb(255, 0, 0)", "first window lowered"); - is(colorBrowser2, "rgb(255, 0, 0)", "second window lowered"); + colorChromeBrowser1 = await getBackgroundColor(chromeBrowser1, false); + colorChromeBrowser2 = await getBackgroundColor(chromeBrowser2, false); + colorHttpBrowser = await getBackgroundColor(httpBrowser, false); + is(colorChromeBrowser1, "rgb(255, 0, 0)", "first tab lowered"); + is(colorChromeBrowser2, "rgb(255, 0, 0)", "second tab lowered"); + is(colorHttpBrowser, "rgb(255, 0, 0)", "third tab lowered"); // Step 3 - close the other window again otherWindow.close(); - colorBrowser1 = await getBackgroundColor(browser1, true); - colorBrowser2 = await getBackgroundColor(browser2, true); - is(colorBrowser1, "rgba(0, 0, 0, 0)", "first window raised"); - is(colorBrowser2, "rgba(0, 0, 0, 0)", "second window raised"); + colorChromeBrowser1 = await getBackgroundColor(chromeBrowser1, true); + colorChromeBrowser2 = await getBackgroundColor(chromeBrowser2, true); + colorHttpBrowser = await getBackgroundColor(httpBrowser, true); + is(colorChromeBrowser1, "rgba(0, 0, 0, 0)", "first tab raised"); + is(colorChromeBrowser2, "rgba(0, 0, 0, 0)", "second tab raised"); + is(colorHttpBrowser, "rgba(0, 0, 0, 0)", "third tab raised"); // Step 4 - switch to the second tab - gBrowser.selectedTab = tab2; - colorBrowser1 = await getBackgroundColor(browser1, false); - colorBrowser2 = await getBackgroundColor(browser2, false); - is(colorBrowser1, "rgba(0, 0, 0, 0)", "first window after tab switch"); - is(colorBrowser2, "rgba(0, 0, 0, 0)", "second window after tab switch"); - - BrowserTestUtils.removeTab(tab1); - BrowserTestUtils.removeTab(tab2); + gBrowser.selectedTab = chromeTab2; + colorChromeBrowser1 = await getBackgroundColor(chromeBrowser1, true); + colorChromeBrowser2 = await getBackgroundColor(chromeBrowser2, true); + colorHttpBrowser = await getBackgroundColor(httpBrowser, true); + is(colorChromeBrowser1, "rgba(0, 0, 0, 0)", "first tab after tab switch"); + is(colorChromeBrowser2, "rgba(0, 0, 0, 0)", "second tab after tab switch"); + is(colorHttpBrowser, "rgba(0, 0, 0, 0)", "third tab after tab switch"); + + BrowserTestUtils.removeTab(chromeTab1); + BrowserTestUtils.removeTab(chromeTab2); + BrowserTestUtils.removeTab(httpTab); otherWindow = null; }); -function getBackgroundColor(browser, ifChanged) { - return SpecialPowers.spawn(browser, [], () => { - return new Promise(resolve => { - let oldColor = null; - let timer = content.setInterval(() => { - let area = content.document.getElementById("area"); - if (!area) { - return; /* hasn't loaded yet */ - } - - let color = content.getComputedStyle(area).backgroundColor; - if (oldColor != color || !ifChanged) { - content.clearInterval(timer); - oldColor = color; - resolve(color); - } - }, 20); - }); - }); +function getBackgroundColor(browser, expectedActive) { + return SpecialPowers.spawn( + browser, + [!expectedActive], + async hasPseudoClass => { + let area = content.document.getElementById("area"); + await ContentTaskUtils.waitForCondition(() => { + return area; + }, "Page has loaded"); + await ContentTaskUtils.waitForCondition(() => { + return area.matches(":-moz-window-inactive") == hasPseudoClass; + }, `Window is considered ${hasPseudoClass ? "inactive" : "active"}`); + + return content.getComputedStyle(area).backgroundColor; + } + ); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/head.js firefox-85.0+build1/browser/base/content/test/general/head.js --- firefox-84.0.2+build1/browser/base/content/test/general/head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/head.js 2021-01-18 19:58:08.000000000 +0000 @@ -377,7 +377,7 @@ async function loadBadCertPage(url) { let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser); - await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url); await loaded; await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { @@ -401,6 +401,7 @@ await TestUtils.waitForCondition(() => { let menu = styleMenu._pageStyleSheets.get(permanentKey); + info(`waiting for sheets: ${menu && menu.filteredStyleSheets.length}`); return menu && menu.filteredStyleSheets.length >= styleSheetCount; }, "waiting for style sheets to load"); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/general/page_style_sample.html firefox-85.0+build1/browser/base/content/test/general/page_style_sample.html --- firefox-84.0.2+build1/browser/base/content/test/general/page_style_sample.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/general/page_style_sample.html 2021-01-18 19:58:08.000000000 +0000 @@ -38,8 +38,8 @@ - + diff -Nru firefox-84.0.2+build1/browser/base/content/test/metaTags/browser.ini firefox-85.0+build1/browser/base/content/test/metaTags/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/metaTags/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/metaTags/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -5,4 +5,5 @@ [browser_bad_meta_tags.js] support-files = bad_meta_tags.html [browser_meta_tags.js] +skip-if = tsan # Bug 1403403 support-files = meta_tags.html diff -Nru firefox-84.0.2+build1/browser/base/content/test/outOfProcess/browser_controller.js firefox-85.0+build1/browser/base/content/test/outOfProcess/browser_controller.js --- firefox-84.0.2+build1/browser/base/content/test/outOfProcess/browser_controller.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/outOfProcess/browser_controller.js 2021-01-18 19:58:08.000000000 +0000 @@ -49,7 +49,7 @@ // need to wait for the focus to have been updated. await SpecialPowers.spawn(browsingContexts[stepNum], [], () => { return ContentTaskUtils.waitForCondition( - () => docShell.isActive && content.document.hasFocus() + () => content.browsingContext.isActive && content.document.hasFocus() ); }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser.ini firefox-85.0+build1/browser/base/content/test/performance/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/performance/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -56,5 +56,3 @@ [browser_window_resize.js] [browser_windowclose.js] [browser_windowopen.js] - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_preferences_usage.js firefox-85.0+build1/browser/base/content/test/performance/browser_preferences_usage.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_preferences_usage.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_preferences_usage.js 2021-01-18 19:58:08.000000000 +0000 @@ -264,7 +264,7 @@ for (let i = 0; i < 50; i++) { let url = urls[i % urls.length]; info(`Navigating to ${url}...`); - await BrowserTestUtils.loadURI(tab.linkedBrowser, url); + BrowserTestUtils.loadURI(tab.linkedBrowser, url); await BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, url); info(`Loaded ${url}.`); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_content.js firefox-85.0+build1/browser/base/content/test/performance/browser_startup_content.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_content.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_startup_content.js 2021-01-18 19:58:08.000000000 +0000 @@ -40,8 +40,7 @@ "resource:///actors/BrowserTabChild.jsm", "resource:///actors/LinkHandlerChild.jsm", "resource:///actors/PageStyleChild.jsm", - "resource:///actors/SearchTelemetryChild.jsm", - "resource://gre/modules/E10SUtils.jsm", + "resource:///actors/SearchSERPTelemetryChild.jsm", "resource://gre/modules/Readerable.jsm", // Telemetry diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_images.js firefox-85.0+build1/browser/base/content/test/performance/browser_startup_images.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_images.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_startup_images.js 2021-01-18 19:58:08.000000000 +0000 @@ -52,7 +52,7 @@ }, { - file: "chrome://browser/skin/chevron.svg", + file: "chrome://global/skin/icons/chevron.svg", platforms: ["win", "linux", "macosx"], intermittentShown: ["win", "linux"], }, diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup.js firefox-85.0+build1/browser/base/content/test/performance/browser_startup.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_startup.js 2021-01-18 19:58:08.000000000 +0000 @@ -82,7 +82,6 @@ denylist: { components: new Set([ "PageIconProtocolHandler.js", - "PlacesCategoriesStarter.js", "nsPlacesExpiration.js", ]), modules: new Set([ diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_mainthreadio.js firefox-85.0+build1/browser/base/content/test/performance/browser_startup_mainthreadio.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_mainthreadio.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_startup_mainthreadio.js 2021-01-18 19:58:08.000000000 +0000 @@ -219,8 +219,7 @@ path: "ProfD:xulstore/data.mdb", condition: WIN, read: 1, - write: 3, - fsync: 1, + write: 1, }, ], @@ -281,12 +280,6 @@ condition: WIN, stat: 1, }, - { - // bug 1546838 - path: "ProfD:xulstore/data.mdb", - condition: WIN, - read: 2, - }, ], // We reach this phase right after showing the first browser window. @@ -305,12 +298,6 @@ stat: 1, }, { - // bug 1586808 - path: "UserPlugins.parent:", - condition: WIN, - stat: 1, - }, - { path: "XREAppFeat:formautofill@mozilla.org.xpi", condition: !WIN, stat: 1, @@ -366,7 +353,7 @@ // bug 1546838 path: "ProfD:xulstore/data.mdb", condition: MAC, - write: 3, + write: 1, }, ], diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_syncIPC.js firefox-85.0+build1/browser/base/content/test/performance/browser_startup_syncIPC.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_startup_syncIPC.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_startup_syncIPC.js 2021-01-18 19:58:08.000000000 +0000 @@ -10,6 +10,10 @@ const WIN = AppConstants.platform == "win"; const MAC = AppConstants.platform == "macosx"; const WEBRENDER = window.windowUtils.layerManagerType == "WebRender"; +const SKELETONUI = Services.prefs.getBoolPref( + "browser.startup.preXulSkeletonUI", + false +); /* * Specifying 'ignoreIfUnused: true' will make the test ignore unused entries; @@ -118,7 +122,10 @@ }, { name: "PGPU::Msg_GetDeviceStatus", - condition: WIN && WEBRENDER, // bug 1553740 might want to drop the WEBRENDER clause here + // bug 1553740 might want to drop the WEBRENDER clause here. + // Additionally, the skeleton UI causes us to attach "before first paint" to a + // later event, which lets this sneak in. + condition: WIN && (WEBRENDER || SKELETONUI), // If Init() completes before we call EnsureGPUReady we won't send GetDeviceStatus // so we can safely ignore if unused. ignoreIfUnused: true, @@ -259,7 +266,7 @@ }, { name: "PCompositorBridge::Msg_FlushRendering", - condition: MAC || LINUX, + condition: MAC || LINUX || SKELETONUI, ignoreIfUnused: true, maxCount: 1, }, diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/browser_windowopen.js firefox-85.0+build1/browser/base/content/test/performance/browser_windowopen.js --- firefox-84.0.2+build1/browser/base/content/test/performance/browser_windowopen.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/browser_windowopen.js 2021-01-18 19:58:08.000000000 +0000 @@ -45,6 +45,8 @@ Services.obs.notifyObservers(null, "startupcache-invalidate"); Services.obs.notifyObservers(null, "chrome-flush-caches"); + let bookmarksToolbarRect = await getBookmarksToolbarRect(); + let win = window.openDialog( AppConstants.BROWSER_CHROME_URL, "_blank", @@ -54,8 +56,6 @@ await disableFxaBadge(); - let bookmarksToolbarRect = await getBookmarksToolbarRect(); - let alreadyFocused = false; let inRange = (val, min, max) => min <= val && val <= max; let expectations = { @@ -101,12 +101,31 @@ }, }, { - name: "bug 1667237 - the bookmarks toolbar shouldn't flicker", + name: "Initial bookmark icon appearing after startup", + condition: r => + r.w == 16 && + r.h == 16 && // icon size + inRange( + r.y1, + bookmarksToolbarRect.top, + bookmarksToolbarRect.top + bookmarksToolbarRect.height / 2 + ) && // in the toolbar + inRange(r.x1, 11, 13), // very close to the left of the screen + }, + { + // Note that the length and x values here are a bit weird because on + // some fonts, we appear to detect the two words separately. + name: + "Initial bookmark text ('Getting Started' or 'Get Involved') appearing after startup", condition: r => - r.y1 >= bookmarksToolbarRect.top && - r.y2 <= bookmarksToolbarRect.bottom && - r.x1 >= bookmarksToolbarRect.left && - r.x2 <= bookmarksToolbarRect.right, + inRange(r.w, 25, 120) && // length of text + inRange(r.h, 9, 15) && // height of text + inRange( + r.y1, + bookmarksToolbarRect.top, + bookmarksToolbarRect.top + bookmarksToolbarRect.height / 2 + ) && // in the toolbar + inRange(r.x1, 30, 90), // close to the left of the screen }, ], }, diff -Nru firefox-84.0.2+build1/browser/base/content/test/performance/io/browser.ini firefox-85.0+build1/browser/base/content/test/performance/io/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/performance/io/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/performance/io/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -3,7 +3,7 @@ # Disabled on Linux asan due to bug 1549729. # Disabled on Windows Arm64 due to bug 1551493. # Disabled on Windows asan due to intermittent startup hangs, bug 1629824. -skip-if = debug || (os == "linux" && asan) || (os == "win" && (asan || processor == "aarch64")) +skip-if = debug || tsan || (os == "linux" && asan) || (os == "win" && (asan || processor == "aarch64")) # to avoid overhead when running the browser normally, startupRecorder.js will # do almost nothing unless browser.startup.record is true. # gfx.canvas.willReadFrequently.enable is just an optimization, but needs to be @@ -18,6 +18,7 @@ environment = GNOME_ACCESSIBILITY=0 MOZ_PROFILER_STARTUP=1 + MOZ_PROFILER_STARTUP_PERFORMANCE_TEST=1 MOZ_PROFILER_STARTUP_FEATURES=js,mainthreadio,ipcmessages MOZ_PROFILER_STARTUP_ENTRIES=10000000 [../browser_startup_mainthreadio.js] diff -Nru firefox-84.0.2+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js firefox-85.0+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js --- firefox-84.0.2+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/permissions/browser_autoplay_blocked.js 2021-01-18 19:58:08.000000000 +0000 @@ -208,7 +208,7 @@ Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.BLOCKED); await BrowserTestUtils.withNewTab("about:home", async function(browser) { - await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE); + BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE); await blockedIconShown(); gBrowser.goBack(); @@ -255,7 +255,7 @@ await BrowserTestUtils.withNewTab("about:home", async function(browser) { await blockedIconHidden(); - await BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE); + BrowserTestUtils.loadURI(browser, AUTOPLAY_PAGE); await blockedIconShown(); Services.prefs.setIntPref(AUTOPLAY_PREF, Ci.nsIAutoplay.ALLOWED); @@ -308,7 +308,7 @@ await BrowserTestUtils.withNewTab("about:home", async function(browser) { await blockedIconHidden(); - await BrowserTestUtils.loadURI(browser, MUTED_AUTOPLAY_PAGE); + BrowserTestUtils.loadURI(browser, MUTED_AUTOPLAY_PAGE); await blockedIconShown(); await openIdentityPopup(); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/blockNoPlugins-plugins.json firefox-85.0+build1/browser/base/content/test/plugins/blockNoPlugins-plugins.json --- firefox-84.0.2+build1/browser/base/content/test/plugins/blockNoPlugins-plugins.json 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/blockNoPlugins-plugins.json 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -[] \ No newline at end of file diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginHard-plugins.json firefox-85.0+build1/browser/base/content/test/plugins/blockPluginHard-plugins.json --- firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginHard-plugins.json 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/blockPluginHard-plugins.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -[ - { - "matchFilename": "libnptest\\.so|nptest\\.dll|Test\\.plugin", - "versionRange": [ - { - "severity": "2" - } - ], - "blockID": "p9999" - } -] \ No newline at end of file diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginInfoURL-plugins.json firefox-85.0+build1/browser/base/content/test/plugins/blockPluginInfoURL-plugins.json --- firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginInfoURL-plugins.json 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/blockPluginInfoURL-plugins.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -[ - { - "matchFilename": "libnptest\\.so|nptest\\.dll|Test\\.plugin", - "versionRange": [ - { - "severity": "2" - } - ], - "blockID": "p9999", - "infoURL": "http://test.url.com/" - } -] \ No newline at end of file diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginVulnerableNoUpdate-plugins.json firefox-85.0+build1/browser/base/content/test/plugins/blockPluginVulnerableNoUpdate-plugins.json --- firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginVulnerableNoUpdate-plugins.json 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/blockPluginVulnerableNoUpdate-plugins.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -[ - { - "matchFilename": "libnptest\\.so|nptest\\.dll|Test\\.plugin", - "versionRange": [ - { - "severity": "0", - "vulnerabilityStatus": "2" - } - ], - "blockID": "p9999" - } -] \ No newline at end of file diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginVulnerableUpdatable-plugins.json firefox-85.0+build1/browser/base/content/test/plugins/blockPluginVulnerableUpdatable-plugins.json --- firefox-84.0.2+build1/browser/base/content/test/plugins/blockPluginVulnerableUpdatable-plugins.json 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/blockPluginVulnerableUpdatable-plugins.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -[ - { - "matchFilename": "libnptest\\.so|nptest\\.dll|Test\\.plugin", - "versionRange": [ - { - "severity": "0", - "vulnerabilityStatus": "1" - } - ], - "blockID": "p9999" - } -] \ No newline at end of file diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_blocking.js firefox-85.0+build1/browser/base/content/test/plugins/browser_blocking.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_blocking.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_blocking.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,433 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); - -function updateAllTestPlugins(aState) { - setTestPluginEnabledState(aState, "Test Plug-in"); - setTestPluginEnabledState(aState, "Second Test Plug-in"); -} - -function promisePluginActivated() { - return SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() { - return ContentTaskUtils.waitForCondition( - () => content.document.getElementById("test").activated, - "Wait for plugin to be activated" - ); - }); -} - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - updateAllTestPlugins(Ci.nsIPluginTag.STATE_ENABLED); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - // Prime the content process - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,hi" - ); -}); - -// Tests a vulnerable, updatable plugin - -add_task(async function() { - // enable hard blocklisting of test - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginVulnerableUpdatable", - gTestBrowser - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, - "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE" - ); - ok(!pluginInfo.activated, "Test 18a, Plugin should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && overlay.classList.contains("visible"), - "Test 18a, Plugin overlay should exist, not be hidden" - ); - - let updateLink = plugin.openOrClosedShadowRoot.getElementById( - "checkForUpdatesLink" - ); - Assert.ok( - updateLink.style.visibility != "hidden", - "Test 18a, Plugin should have an update link" - ); - }); - - let promise = BrowserTestUtils.waitForEvent( - gBrowser.tabContainer, - "TabOpen", - true - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let updateLink = plugin.openOrClosedShadowRoot.getElementById( - "checkForUpdatesLink" - ); - let bounds = updateLink.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - await promise; - - promise = BrowserTestUtils.waitForEvent( - gBrowser.tabContainer, - "TabClose", - true - ); - gBrowser.removeCurrentTab(); - await promise; -}); - -add_task(async function() { - // clicking the update link should not activate the plugin - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, - "Test 18a, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE" - ); - ok(!pluginInfo.activated, "Test 18b, Plugin should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && overlay.classList.contains("visible"), - "Test 18b, Plugin overlay should exist, not be hidden" - ); - }); -}); - -// Tests a vulnerable plugin with no update -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginVulnerableNoUpdate", - gTestBrowser - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 18c, Should have a click-to-play notification"); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE, - "Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE" - ); - ok(!pluginInfo.activated, "Test 18c, Plugin should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && overlay.classList.contains("visible"), - "Test 18c, Plugin overlay should exist, not be hidden" - ); - - let updateLink = plugin.openOrClosedShadowRoot.getElementById( - "checkForUpdatesLink" - ); - Assert.ok( - updateLink && updateLink.style.display != "block", - "Test 18c, Plugin should not have an update link" - ); - }); - - // check that click "Allow" works with blocked plugins - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - await promisePluginActivated(); - pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE, - "Test 18c, plugin fallback type should be PLUGIN_VULNERABLE_NO_UPDATE" - ); - ok(pluginInfo.activated, "Test 18c, Plugin should be activated"); - let enabledState = getTestPluginEnabledState(); - ok( - enabledState, - "Test 18c, Plugin enabled state should be STATE_CLICKTOPLAY" - ); -}); - -// continue testing "Always allow", make sure it sticks. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 18d, Waited too long for plugin to activate"); - - clearAllPluginPermissions(); -}); - -// clicking the in-content overlay of a vulnerable plugin should bring -// up the notification and not directly activate the plugin -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 18f, Should have a click-to-play notification"); - ok(notification.dismissed, "Test 18f, notification should start dismissed"); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 18f, Waited too long for plugin to activate"); - - var oldEventCallback = notification.options.eventCallback; - let promise = promiseForCondition(() => oldEventCallback == null); - notification.options.eventCallback = function() { - if (oldEventCallback) { - oldEventCallback(); - } - oldEventCallback = null; - }; - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let bounds = plugin.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - await promise; - - ok(notification, "Test 18g, Should have a click-to-play notification"); - ok(!notification.dismissed, "Test 18g, notification should be open"); - - pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated"); -}); - -// Test that "always allow"-ing a plugin will not allow it when it becomes -// blocklisted. -add_task(async function() { - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 24a, Should have a click-to-play notification"); - - // Plugin should start as CTP - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "Test 24a, plugin fallback type should be PLUGIN_CLICK_TO_PLAY" - ); - ok(!pluginInfo.activated, "Test 24a, Plugin should not be active."); - - // simulate "allow" - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - await promisePluginActivated(); - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 24a, Plugin should be active."); - - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginVulnerableUpdatable", - gTestBrowser - ); -}); - -// the plugin is now blocklisted, so it should not automatically load -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 24b, Should have a click-to-play notification"); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, - "Test 24b, plugin fallback type should be PLUGIN_VULNERABLE_UPDATABLE" - ); - ok(!pluginInfo.activated, "Test 24b, Plugin should not be active."); - - // simulate "allow" - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - await promisePluginActivated(); - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 24b, Plugin should be active."); - - clearAllPluginPermissions(); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); -}); - -// Plugin sync removal test. Note this test produces a notification drop down since -// the plugin we add has zero dims. -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_syncRemoved.html" - ); - - // Maybe there some better trick here, we need to wait for the page load, then - // wait for the js to execute in the page. - await waitForMs(500); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins" - ); - ok( - notification, - "Test 25: There should be a plugin notification even if the plugin was immediately removed" - ); - ok( - notification.dismissed, - "Test 25: The notification should be dismissed by default" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,hi" - ); -}); - -// Tests a page with a blocked plugin in it and make sure the infoURL property -// the blocklist file gets used. -add_task(async function() { - clearAllPluginPermissions(); - - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginInfoURL", - gTestBrowser - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins" - ); - - // Since the plugin notification is dismissed by default, reshow it. - await promiseForNotificationShown(notification); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED, - "Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - Assert.ok(!plugin.activated, "Plugin should not be activated."); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_blocklist_content.js firefox-85.0+build1/browser/base/content/test/plugins/browser_blocklist_content.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_blocklist_content.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_blocklist_content.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -var gTestBrowser = null; -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gChromeRoot = getRootDirectory(gTestPath); - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - - // Prime the blocklist service, the remote service doesn't launch on startup. - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); -}); - -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let test = content.document.getElementById("test"); - Assert.ok(test.activated, "task 1a: test plugin should be activated!"); - }); -}); - -// Load a fresh page, load a new plugin blocklist, then load the same page again. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,GO!" - ); - await asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard", gTestBrowser); - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let test = content.document.getElementById("test"); - ok(!test.activated, "task 2a: test plugin shouldn't activate!"); - }); -}); - -// Unload the block list and lets do this again, only this time lets -// hack around in the content blocklist service maliciously. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,GO!" - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); - - // Hack the planet! Load our blocklist shim, so we can mess with blocklist - // return results in the content process. Active until we close our tab. - let base = gChromeRoot.slice(0, -1); - - let actor = { - child: { - moduleURI: `${base}/BlocklistTestProxy.jsm`, - observer: ["webnavigation-create"], - }, - }; - ChromeUtils.registerProcessActor("BlocklistTestProxy", actor); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let test = content.document.getElementById("test"); - Assert.ok(test.activated, "task 3a: test plugin should be activated!"); - }); - - registerCleanupFunction(async function() { - let dp = - gBrowser.selectedBrowser.browsingContext.currentWindowGlobal.domProcess; - await dp.getActor("BlocklistTestProxy").sendQuery("unload"); - - ChromeUtils.unregisterProcessActor("BlocklistTestProxy", actor); - }); -}); - -// Load a fresh page, load a new plugin blocklist, then load the same page again. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,GO!" - ); - await asyncSetAndUpdateBlocklist(gTestRoot + "blockPluginHard", gTestBrowser); - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let test = content.document.getElementById("test"); - Assert.ok(!test.activated, "task 4a: test plugin shouldn't activate!"); - }); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug743421.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug743421.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug743421.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug743421.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - let newTab = BrowserTestUtils.addTab(gBrowser); - gBrowser.selectedTab = newTab; - gTestBrowser = gBrowser.selectedBrowser; - - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_CLICKTOPLAY, - "Second Test Plug-in" - ); - - // Prime the blocklist service, the remote service doesn't launch on startup. - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); -}); - -// Tests that navigation within the page and the window.history API doesn't break click-to-play state. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_add_dynamically.html" - ); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(!notification, "Test 1a, Should not have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin()); - }); - - await promisePopupNotification("click-to-play-plugins"); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementsByTagName("embed")[0]; - Assert.ok(!plugin.activated, "Test 1b, Plugin should not be activated"); - }); - - // Click the activate button on doorhanger to make sure it works - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementsByTagName("embed")[0]; - Assert.ok(plugin.activated, "Test 1b, Plugin should be activated"); - }); -}); - -add_task(async function() { - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 1c, Should still have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin()); - let plugin = content.document.getElementsByTagName("embed")[1]; - Assert.ok( - plugin.activated, - "Test 1c, Newly inserted plugin in activated page should be activated" - ); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementsByTagName("embed")[1]; - Assert.ok(plugin.activated, "Test 1d, Plugin should be activated"); - - let promise = ContentTaskUtils.waitForEvent(content, "hashchange"); - content.location += "#anchorNavigation"; - await promise; - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin()); - let plugin = content.document.getElementsByTagName("embed")[2]; - Assert.ok(plugin.activated, "Test 1e, Plugin should be activated"); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementsByTagName("embed")[2]; - Assert.ok(plugin.activated, "Test 1f, Plugin should be activated"); - - content.history.replaceState({}, "", "replacedState"); - new XPCNativeWrapper(XPCNativeWrapper.unwrap(content).addPlugin()); - plugin = content.document.getElementsByTagName("embed")[3]; - Assert.ok(plugin.activated, "Test 1g, Plugin should be activated"); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug744745.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug744745.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug744745.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug744745.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let promisePluginBindingAttached = BrowserTestUtils.waitForContentEvent( - gTestBrowser, - "PluginBindingAttached", - true, - null, - true - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_bug744745.html" - ); - - await promisePluginBindingAttached; - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - if (!plugin) { - Assert.ok(false, "plugin element not available."); - return; - } - // We can't use MochiKit's routine - let style = content.getComputedStyle(plugin); - Assert.ok( - "opacity" in style && style.opacity == 1, - "plugin style properly configured." - ); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug787619.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug787619.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug787619.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug787619.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; -var gWrapperClickCount = 0; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let testRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" - ); - await promiseTabLoadEvent( - gBrowser.selectedTab, - testRoot + "plugin_bug787619.html" - ); - - // Due to layout being async, "PluginBindAttached" may trigger later. - // This forces a layout flush, thus triggering it, and schedules the - // test so it is definitely executed afterwards. - await promiseUpdatePluginBindings(gTestBrowser); - - // check plugin state - let pluginInfo = await promiseForPluginInfo("plugin"); - ok(!pluginInfo.activated, "1a plugin should not be activated"); - - // click the overlay to prompt - let promise = promisePopupNotification("click-to-play-plugins"); - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("plugin"); - let bounds = plugin.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - await promise; - - // check plugin state - pluginInfo = await promiseForPluginInfo("plugin"); - ok(!pluginInfo.activated, "1b plugin should not be activated"); - - let condition = () => - !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed && PopupNotifications.panel.firstElementChild; - await promiseForCondition(condition); - PopupNotifications.panel.firstElementChild.button.click(); - - // check plugin state - pluginInfo = await promiseForPluginInfo("plugin"); - ok(pluginInfo.activated, "plugin should be activated"); - - is(gWrapperClickCount, 0, "wrapper should not have received any clicks"); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug797677.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug797677.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug797677.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug797677.js 2021-01-18 19:58:08.000000000 +0000 @@ -7,12 +7,6 @@ add_task(async function() { registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); Services.console.unregisterListener(errorListener); gBrowser.removeCurrentTab(); window.focus(); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug812562.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug812562.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug812562.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug812562.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; - -add_task(async function() { - await SpecialPowers.pushPrefEnv({ - set: [["browser.navigation.requireUserInteraction", false]], - }); - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - // Prime the blocklist service, the remote service doesn't launch on startup. - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); -}); - -// Tests that the going back will reshow the notification for click-to-play -// blocklisted plugins -add_task(async function() { - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginVulnerableUpdatable", - gTestBrowser - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - popupNotification, - "test part 1: Should have a click-to-play notification" - ); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, - "plugin should be marked as VULNERABLE" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - Assert.ok( - !!content.document.getElementById("test"), - "test part 1: plugin should not be activated" - ); - }); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); -}); - -add_task(async function() { - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "test part 2: Should not have a click-to-play notification" - ); - await SpecialPowers.spawn(gTestBrowser, [], async function() { - Assert.ok( - !content.document.getElementById("test"), - "test part 2: plugin should not be activated" - ); - }); - - let obsPromise = TestUtils.topicObserved( - "PopupNotifications-updateNotShowing" - ); - let overlayPromise = promisePopupNotification("click-to-play-plugins"); - gTestBrowser.goBack(); - await obsPromise; - await overlayPromise; -}); - -add_task(async function() { - await promiseUpdatePluginBindings(gTestBrowser); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - popupNotification, - "test part 3: Should have a click-to-play notification" - ); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, - "plugin should be marked as VULNERABLE" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - Assert.ok( - !!content.document.getElementById("test"), - "test part 3: plugin should not be activated" - ); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug818118.js firefox-85.0+build1/browser/base/content/test/plugins/browser_bug818118.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_bug818118.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_bug818118.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_both.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(popupNotification, "should have a click-to-play notification"); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "plugin should be click to play" - ); - ok(!pluginInfo.activated, "plugin should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], () => { - let unknown = content.document.getElementById("unknown"); - ok(unknown, "should have unknown plugin in page"); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata.html firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ - - - - Plugin Clear Site Data sanitize test - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata.js firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); -var gTestBrowser = null; - -// Test clearing plugin data using Sanitizer.jsm. -const testURL1 = gTestRoot + "browser_clearplugindata.html"; -const testURL2 = gTestRoot + "browser_clearplugindata_noage.html"; - -const { Sanitizer } = ChromeUtils.import("resource:///modules/Sanitizer.jsm"); - -const pluginHostIface = Ci.nsIPluginHost; -var pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); -pluginHost.QueryInterface(pluginHostIface); - -var pluginTag = getTestPlugin(); - -function stored(needles) { - let something = pluginHost.siteHasData(this.pluginTag, null); - if (!needles) { - return something; - } - - if (!something) { - return false; - } - - for (let i = 0; i < needles.length; ++i) { - if (!pluginHost.siteHasData(this.pluginTag, needles[i])) { - return false; - } - } - return true; -} - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - if (gTestBrowser) { - gBrowser.removeCurrentTab(); - } - window.focus(); - gTestBrowser = null; - }); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); -}); - -function setPrefs(cookies, pluginData) { - let itemPrefs = Services.prefs.getBranch("privacy.cpd."); - itemPrefs.setBoolPref("history", false); - itemPrefs.setBoolPref("downloads", false); - itemPrefs.setBoolPref("cache", false); - itemPrefs.setBoolPref("cookies", cookies); - itemPrefs.setBoolPref("formdata", false); - itemPrefs.setBoolPref("offlineApps", false); - itemPrefs.setBoolPref("passwords", false); - itemPrefs.setBoolPref("sessions", false); - itemPrefs.setBoolPref("siteSettings", false); - itemPrefs.setBoolPref("pluginData", pluginData); -} - -async function testClearingData(url) { - // Load page to set data for the plugin. - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - await promiseTabLoadEvent(gBrowser.selectedTab, url); - - await promiseUpdatePluginBindings(gTestBrowser); - - ok( - stored(["foo.com", "bar.com", "baz.com", "qux.com"]), - "Data stored for sites" - ); - - // Clear 20 seconds ago. - // In the case of testURL2 the plugin will throw - // NS_ERROR_PLUGIN_TIME_RANGE_NOT_SUPPORTED, which should result in us - // clearing all data regardless of age. - let now_uSec = Date.now() * 1000; - let range = [now_uSec - 20 * 1000000, now_uSec]; - await Sanitizer.sanitize(null, { range, ignoreTimespan: false }); - - if (url == testURL1) { - ok(stored(["bar.com", "qux.com"]), "Data stored for sites"); - ok(!stored(["foo.com"]), "Data cleared for foo.com"); - ok(!stored(["baz.com"]), "Data cleared for baz.com"); - - // Clear everything. - await Sanitizer.sanitize(null, { ignoreTimespan: false }); - } - - ok(!stored(null), "All data cleared"); - - gBrowser.removeCurrentTab(); - gTestBrowser = null; -} - -add_task(async function() { - // Test when sanitizing cookies. - await setPrefs(true, false); - await testClearingData(testURL1); - await testClearingData(testURL2); - - // Test when sanitizing pluginData. - await setPrefs(false, true); - await testClearingData(testURL1); - await testClearingData(testURL2); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata_noage.html firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata_noage.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_clearplugindata_noage.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_clearplugindata_noage.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ - - - - Plugin Clear Site Data sanitize test without age - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_context_menu.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_context_menu.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_context_menu.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_context_menu.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -// Test that the activate action in content menus for CTP plugins works -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - let bindingPromise = BrowserTestUtils.waitForContentEvent( - gBrowser.selectedBrowser, - "PluginBindingAttached", - true, - null, - true - ); - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); - await bindingPromise; - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - ok(popupNotification, "Test 1, Should have a click-to-play notification"); - - // check plugin state - let pluginInfo = await promiseForPluginInfo("test", gBrowser.selectedBrowser); - ok(!pluginInfo.activated, "plugin should not be activated"); - - // Display a context menu on the test plugin so we can test - // activation menu options. - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - let bounds = plugin.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("contextmenu", left, top, 2, 1, 0); - }); - - popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - ok(popupNotification, "Should have a click-to-play notification"); - ok(popupNotification.dismissed, "notification should be dismissed"); - - // fixes a occasional test timeout on win7 opt - await promiseForCondition(() => document.getElementById("context-ctp-play")); - - let actMenuItem = document.getElementById("context-ctp-play"); - ok(actMenuItem, "Should have a context menu entry for activating the plugin"); - - // Activate the plugin via the context menu - EventUtils.synthesizeMouseAtCenter(actMenuItem, {}); - - await promiseForCondition( - () => - !PopupNotifications.panel.dismissed && - PopupNotifications.panel.firstElementChild - ); - - // Activate the plugin - PopupNotifications.panel.firstElementChild.button.click(); - - // check plugin state - pluginInfo = await promiseForPluginInfo("test", gBrowser.selectedBrowser); - ok(pluginInfo.activated, "plugin should be activated"); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_crashreporting.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_crashreporting.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_crashreporting.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_crashreporting.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -const SERVER_URL = - "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; -const PLUGIN_PAGE = gTestRoot + "plugin_big.html"; -const PLUGIN_SMALL_PAGE = gTestRoot + "plugin_small.html"; - -/** - * Takes an nsIPropertyBag and converts it into a JavaScript Object. It - * will also convert any nsIPropertyBag's within the nsIPropertyBag - * recursively. - * - * @param aBag - * The nsIPropertyBag to convert. - * @return Object - * Keyed on the names of the nsIProperty's within the nsIPropertyBag, - * and mapping to their values. - */ -function convertPropertyBag(aBag) { - let result = {}; - for (let { name, value } of aBag.enumerator) { - if (value instanceof Ci.nsIPropertyBag) { - value = convertPropertyBag(value); - } - result[name] = value; - } - return result; -} - -add_task(async function setup() { - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables plugin - // crash reports. This test needs them enabled. The test also needs a mock - // report server, and fortunately one is already set up by toolkit/ - // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, - // which CrashSubmit.jsm uses as a server override. - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); - let serverURL = env.get("MOZ_CRASHREPORTER_URL"); - env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); - env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); - - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - registerCleanupFunction(function cleanUp() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); - env.set("MOZ_CRASHREPORTER_URL", serverURL); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - window.focus(); - }); -}); - -/** - * Test that plugin crash submissions still work properly after - * click-to-play activation. - */ -add_task(async function() { - await BrowserTestUtils.withNewTab(PLUGIN_PAGE, async function(browser) { - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(browser); - - let pluginInfo = await promiseForPluginInfo("test", browser); - ok(!pluginInfo.activated, "Plugin should not be activated"); - - // Simulate clicking the "Allow Always" button. - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - browser - ); - await promiseForNotificationShown(notification, browser); - PopupNotifications.panel.firstElementChild.button.click(); - - // Prepare a crash report topic observer that only returns when - // the crash report has been successfully sent. - let crashReportChecker = (subject, data) => { - return data == "success"; - }; - let crashReportPromise = TestUtils.topicObserved( - "crash-report-status", - crashReportChecker - ); - - await SpecialPowers.spawn(browser, [], async function() { - let plugin = content.document.getElementById("test"); - - await ContentTaskUtils.waitForCondition(() => { - return plugin.activated; - }, "Waited too long for plugin to activate."); - - try { - Cu.waiveXrays(plugin).crash(); - } catch (e) {} - - // Wait for the shadow DOM to be connected. - await ContentTaskUtils.waitForCondition( - () => plugin.openOrClosedShadowRoot, - "Need plugin shadow root" - ); - - let getUI = id => { - return plugin.openOrClosedShadowRoot.getElementById(id); - }; - - // Now wait until the plugin crash report UI shows itself, which is - // asynchronous. - let statusDiv; - - await ContentTaskUtils.waitForCondition(() => { - statusDiv = getUI("submitStatus"); - return statusDiv?.getAttribute("status") == "please"; - }, "Waited too long for plugin to show crash report UI"); - - // Make sure the UI matches our expectations... - let style = content.getComputedStyle(getUI("pleaseSubmit")); - if (style.display != "block") { - throw new Error( - `Submission UI visibility is not correct. ` + - `Expected block style, got ${style.display}.` - ); - } - - // Fill the crash report in with some test values that we'll test for in - // the parent. - getUI("submitComment").value = "a test comment"; - let optIn = getUI("submitURLOptIn"); - if (!optIn.checked) { - throw new Error("URL opt-in should default to true."); - } - - // Submit the report. - optIn.click(); - getUI("submitButton").click(); - - // And wait for the parent to say that the crash report was submitted - // successfully. This can take time on debug builds. - await ContentTaskUtils.waitForCondition( - () => { - return statusDiv.getAttribute("status") == "success"; - }, - "Timed out waiting for plugin binding to be in success state", - 100, - 200 - ); - }); - - let [subject] = await crashReportPromise; - - ok( - subject instanceof Ci.nsIPropertyBag, - "The crash report subject should be an nsIPropertyBag." - ); - - let crashData = convertPropertyBag(subject); - ok(crashData.serverCrashID, "Should have a serverCrashID set."); - - // Remove the submitted report file after ensuring it exists. - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(Services.crashmanager._submittedDumpsDir); - file.append(crashData.serverCrashID + ".txt"); - ok(file.exists(), "Submitted report file should exist"); - file.remove(false); - - ok(crashData.extra, "Extra data should exist"); - is( - crashData.extra.PluginUserComment, - "a test comment", - "Comment in extra data should match comment in textbox" - ); - - is( - crashData.extra.PluginContentURL, - undefined, - "URL should be absent from extra data when opt-in not checked" - ); - }); -}); - -/** - * Test that plugin crash submissions still work properly after - * click-to-play with the notification bar. - */ -add_task(async function() { - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: PLUGIN_SMALL_PAGE, - }, - async function(browser) { - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(browser); - - let pluginInfo = await promiseForPluginInfo("test", browser); - ok(pluginInfo.activated, "Plugin should be activated from previous test"); - - // Prepare a crash report topic observer that only returns when - // the crash report has been successfully sent. - let crashReportChecker = (subject, data) => { - return data == "success"; - }; - let crashReportPromise = TestUtils.topicObserved( - "crash-report-status", - crashReportChecker - ); - - await SpecialPowers.spawn(browser, [], async function() { - let plugin = content.document.getElementById("test"); - - await ContentTaskUtils.waitForCondition(() => { - return plugin.activated; - }, "Waited too long for plugin to activate."); - - try { - Cu.waiveXrays(plugin).crash(); - } catch (e) {} - }); - - // Wait for the notification bar to be displayed. - let notification = await waitForNotificationBar( - "plugin-crashed", - browser - ); - - // Then click the button to submit the crash report. - let buttons = notification.querySelectorAll(".notification-button"); - is(buttons.length, 2, "Should have two buttons."); - - // The "Submit Crash Report" button should be the second one. - let submitButton = buttons[1]; - submitButton.click(); - - let [subject] = await crashReportPromise; - - ok( - subject instanceof Ci.nsIPropertyBag, - "The crash report subject should be an nsIPropertyBag." - ); - - let crashData = convertPropertyBag(subject); - ok(crashData.serverCrashID, "Should have a serverCrashID set."); - - // Remove the submitted report file after ensuring it exists. - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(Services.crashmanager._submittedDumpsDir); - file.append(crashData.serverCrashID + ".txt"); - ok(file.exists(), "Submitted report file should exist"); - file.remove(false); - - is( - crashData.extra.PluginContentURL, - undefined, - "URL should be absent from extra data when opt-in not checked" - ); - } - ); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_drag_drop.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_drag_drop.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_drag_drop.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_drag_drop.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gNewWindow = null; - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gNewWindow.close(); - await BrowserTestUtils.waitForEvent(gNewWindow, "unload", true); - gNewWindow = null; - window.focus(); - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); - - await promisePopupNotification("click-to-play-plugins"); -}); - -add_task(async function() { - gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab); - - // XXX technically can't load fire before we get this call??? - await BrowserTestUtils.waitForEvent(gNewWindow, "load", true); - - await promisePopupNotification( - "click-to-play-plugins", - gNewWindow.gBrowser.selectedBrowser - ); - - ok( - PopupNotifications.getNotification( - "click-to-play-plugins", - gNewWindow.gBrowser.selectedBrowser - ), - "Should have a click-to-play notification in the tab in the new window" - ); - ok( - !PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ), - "Should not have a click-to-play notification in the old window now" - ); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gBrowser.swapBrowsersAndCloseOther( - gBrowser.selectedTab, - gNewWindow.gBrowser.selectedTab - ); - - await promisePopupNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - - ok( - PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ), - "Should have a click-to-play notification in the initial tab again" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); -}); - -add_task(async function() { - await promisePopupNotification("click-to-play-plugins"); - - gNewWindow = gBrowser.replaceTabWithWindow(gBrowser.selectedTab); - - await promiseWaitForFocus(gNewWindow); - - await promisePopupNotification( - "click-to-play-plugins", - gNewWindow.gBrowser.selectedBrowser - ); -}); - -add_task(async function() { - ok( - PopupNotifications.getNotification( - "click-to-play-plugins", - gNewWindow.gBrowser.selectedBrowser - ), - "Should have a click-to-play notification in the tab in the new window" - ); - ok( - !PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ), - "Should not have a click-to-play notification in the old window now" - ); - - let pluginInfo = await promiseForPluginInfo( - "test", - gNewWindow.gBrowser.selectedBrowser - ); - ok(!pluginInfo.activated, "plugin should not be activated"); - - await SpecialPowers.spawn( - gNewWindow.gBrowser.selectedBrowser, - [], - async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let bounds = plugin.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - } - ); - - let condition = () => - !PopupNotifications.getNotification( - "click-to-play-plugins", - gNewWindow.gBrowser.selectedBrowser - ).dismissed && gNewWindow.PopupNotifications.panel.firstElementChild; - await promiseForCondition(condition); -}); - -add_task(async function() { - // Click the activate button on doorhanger to make sure it works - gNewWindow.PopupNotifications.panel.firstElementChild.button.click(); - - let pluginInfo = await promiseForPluginInfo( - "test", - gNewWindow.gBrowser.selectedBrowser - ); - ok(pluginInfo.activated, "plugin should be activated"); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_favorfallback.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_favorfallback.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_favorfallback.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_favorfallback.js 2021-01-18 19:58:08.000000000 +0000 @@ -8,7 +8,6 @@ add_task(async function() { registerCleanupFunction(function() { clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Shockwave Flash"); Services.prefs.clearUserPref("plugins.favorfallback.mode"); Services.prefs.clearUserPref("plugins.favorfallback.rules"); }); @@ -16,10 +15,6 @@ add_task(async function() { Services.prefs.setCharPref("plugins.favorfallback.mode", "follow-ctp"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_CLICKTOPLAY, - "Shockwave Flash" - ); }); /* The expected behavior of each testcase is documented with its markup @@ -81,8 +76,8 @@ ); is( ctpPlugin.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "Plugin is CTP" + Ci.nsIObjectLoadingContent.PLUGIN_ALTERNATE, + "Plugins always use alternate content" ); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_hide_overlay.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_hide_overlay.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_hide_overlay.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_hide_overlay.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_CLICKTOPLAY, - "Second Test Plug-in" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); - - // Tests that the overlay can be hidden for plugins using the close icon. - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon"); - let bounds = closeIcon.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - - Assert.ok( - !overlay.classList.contains("visible"), - "overlay should be hidden." - ); - }); -}); - -// Test that the overlay cannot be interacted with after the user closes the overlay -add_task(async function() { - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_CLICKTOPLAY, - "Second Test Plug-in" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon"); - let closeIconBounds = closeIcon.getBoundingClientRect(); - let overlayBounds = overlay.getBoundingClientRect(); - let overlayLeft = (overlayBounds.left + overlayBounds.right) / 2; - let overlayTop = (overlayBounds.left + overlayBounds.right) / 2; - let closeIconLeft = (closeIconBounds.left + closeIconBounds.right) / 2; - let closeIconTop = (closeIconBounds.top + closeIconBounds.bottom) / 2; - let utils = content.windowUtils; - // Simulate clicking on the close icon. - utils.sendMouseEvent( - "mousedown", - closeIconLeft, - closeIconTop, - 0, - 1, - 0, - false, - 0, - 0 - ); - utils.sendMouseEvent( - "mouseup", - closeIconLeft, - closeIconTop, - 0, - 1, - 0, - false, - 0, - 0 - ); - - // Simulate clicking on the overlay. - utils.sendMouseEvent( - "mousedown", - overlayLeft, - overlayTop, - 0, - 1, - 0, - false, - 0, - 0 - ); - utils.sendMouseEvent( - "mouseup", - overlayLeft, - overlayTop, - 0, - 1, - 0, - false, - 0, - 0 - ); - - Assert.ok( - overlay.hasAttribute("dismissed") && - !overlay.classList.contains("visible"), - "Overlay should be hidden" - ); - }); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins" - ); - - ok(notification.dismissed, "No notification should be shown"); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_iframe.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_iframe.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_iframe.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_iframe.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_iframe.html" - ); - - // Tests that the overlays are visible and actionable if the plugin is in an iframe. - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let frame = content.document.getElementById("frame"); - let doc = frame.contentDocument; - let plugin = doc.getElementById("test"); - await ContentTaskUtils.waitForCondition( - () => plugin.openOrClosedShadowRoot?.getElementById("main"), - "Wait for plugin shadow root" - ); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - plugin && overlay.classList.contains("visible"), - "Test 1, Plugin overlay should exist, not be hidden" - ); - - let closeIcon = plugin.openOrClosedShadowRoot.getElementById("closeIcon"); - let bounds = closeIcon.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = doc.defaultView.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - Assert.ok( - !overlay.classList.contains("visible"), - "Test 1, Plugin overlay should exist, be hidden" - ); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_nonplugins.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_nonplugins.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_nonplugins.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_nonplugins.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED, "Test Plug-in"); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_two_types.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gBrowser.selectedBrowser); - - // Test that the click-to-play notification is not shown for non-plugin object elements - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - ok(popupNotification, "Test 1, Should have a click-to-play notification"); - - let pluginRemovedPromise = BrowserTestUtils.waitForContentEvent( - gBrowser.selectedBrowser, - "PluginRemoved", - true, - null, - true - ); - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let plugin = content.document.getElementById("secondtestA"); - plugin.remove(); - plugin = content.document.getElementById("secondtestB"); - plugin.remove(); - - let image = content.document.createElement("object"); - image.type = "image/png"; - image.data = "moz.png"; - content.document.body.appendChild(image); - }); - await pluginRemovedPromise; - - popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - ok(popupNotification, "Test 2, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - plugin.remove(); - }); - - popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gBrowser.selectedBrowser - ); - ok( - popupNotification, - "Test 3, Should still have a click-to-play notification" - ); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_outsideScrollArea.js 2021-01-18 19:58:08.000000000 +0000 @@ -9,11 +9,6 @@ add_task(async function() { registerCleanupFunction(function() { clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); gBrowser.removeCurrentTab(); window.focus(); @@ -27,22 +22,13 @@ let newTab = BrowserTestUtils.addTab(gBrowser); gBrowser.selectedTab = newTab; gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "Test 1, Should not have a click-to-play notification" - ); }); -// Test that the click-to-play overlay is not hidden for elements -// partially or fully outside the viewport. +// Test that the plugin "blockall" overlay is always present but hidden, +// regardless of whether the overlay is fully, partially, or not in the +// viewport. +// fully in viewport add_task(async function() { await promiseTabLoadEvent( gBrowser.selectedTab, @@ -54,7 +40,7 @@ let p = doc.createElement("embed"); p.setAttribute("id", "test"); - p.setAttribute("type", "application/x-test"); + p.setAttribute("type", "application/x-shockwave-flash"); p.style.left = "0"; p.style.bottom = "200px"; @@ -64,20 +50,16 @@ // Work around for delayed PluginBindingAttached await promiseUpdatePluginBindings(gTestBrowser); - await promisePopupNotification("click-to-play-plugins"); - await SpecialPowers.spawn(gTestBrowser, [], async function() { let plugin = content.document.getElementById("test"); let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && - overlay.classList.contains("visible") && - overlay.getAttribute("sizing") != "blank", - "Test 2, overlay should be visible." - ); + Assert.ok(overlay); + Assert.ok(!overlay.getAttribute("visible")); + Assert.ok(overlay.getAttribute("blockall") == "blockall"); }); }); +// partially in viewport add_task(async function() { await promiseTabLoadEvent( gBrowser.selectedTab, @@ -89,7 +71,7 @@ let p = doc.createElement("embed"); p.setAttribute("id", "test"); - p.setAttribute("type", "application/x-test"); + p.setAttribute("type", "application/x-shockwave-flash"); p.style.left = "0"; p.style.bottom = "-410px"; @@ -99,20 +81,16 @@ // Work around for delayed PluginBindingAttached await promiseUpdatePluginBindings(gTestBrowser); - await promisePopupNotification("click-to-play-plugins"); - await SpecialPowers.spawn(gTestBrowser, [], async function() { let plugin = content.document.getElementById("test"); let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && - overlay.classList.contains("visible") && - overlay.getAttribute("sizing") != "blank", - "Test 3, overlay should be visible." - ); + Assert.ok(overlay); + Assert.ok(!overlay.getAttribute("visible")); + Assert.ok(overlay.getAttribute("blockall") == "blockall"); }); }); +// not in viewport add_task(async function() { await promiseTabLoadEvent( gBrowser.selectedTab, @@ -124,7 +102,7 @@ let p = doc.createElement("embed"); p.setAttribute("id", "test"); - p.setAttribute("type", "application/x-test"); + p.setAttribute("type", "application/x-shockwave-flash"); p.style.left = "-600px"; p.style.bottom = "0"; @@ -134,13 +112,11 @@ // Work around for delayed PluginBindingAttached await promiseUpdatePluginBindings(gTestBrowser); - await promisePopupNotification("click-to-play-plugins"); await SpecialPowers.spawn(gTestBrowser, [], async function() { let plugin = content.document.getElementById("test"); let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - !overlay || overlay.getAttribute("sizing") == "blank", - "Test 4, overlay should be blank." - ); + Assert.ok(overlay); + Assert.ok(!overlay.getAttribute("visible")); + Assert.ok(overlay.getAttribute("blockall") == "blockall"); }); }); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_overlay_styles.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_overlay_styles.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_overlay_styles.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_overlay_styles.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -/* 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/. */ - -"use strict"; - -/* This test ensures that the click-to-play "Activate Plugin" overlay - * is shown in the right style (which is dependent on its size). - */ - -const rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); - -var gTestBrowser = null; - -const gTestcases = { - // 10x10 - testcase1: { - sizing: "blank", - notext: null, - }, - - // 40x40 - testcase2: { - sizing: "tiny", - notext: "notext", - }, - - // 100x70 - testcase3: { - sizing: "reduced", - notext: "notext", - }, - - // 200x200 - testcase4: { - sizing: null, - notext: "notext", - }, - - // 300x300 - testcase5: { - sizing: null, - notext: null, - }, -}; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - gBrowser.removeCurrentTab(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "Sanity check, should not have a click-to-play notification" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_overlay_styles.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [gTestcases], async function( - testcases - ) { - let doc = content.document; - - for (let testcaseId of Object.keys(testcases)) { - let plugin = doc.querySelector(`#${testcaseId} > object`); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok(overlay, `overlay exists in ${testcaseId}`); - - let expectations = testcases[testcaseId]; - - Assert.ok( - overlay.classList.contains("visible"), - `The expected visibility is correct in ${testcaseId}` - ); - - Assert.ok( - overlay.getAttribute("sizing") == expectations.sizing, - `The expected sizing is correct in ${testcaseId}` - ); - - Assert.ok( - overlay.getAttribute("notext") == expectations.notext, - `The expected notext is correct in ${testcaseId}` - ); - } - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_resize.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_resize.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_resize.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_resize.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); - gBrowser.removeCurrentTab(); - window.focus(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - let newTab = BrowserTestUtils.addTab(gBrowser); - gBrowser.selectedTab = newTab; - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "Test 1, Should not have a click-to-play notification" - ); - - await promiseTabLoadEvent(newTab, gTestRoot + "plugin_small.html"); // 10x10 plugin - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); -}); - -// Test that the overlay is hidden for "small" plugin elements and is shown -// once they are resized to a size that can hold the overlay -add_task(async function() { - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(popupNotification, "Test 2, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - !overlay || overlay.getAttribute("sizing") == "blank", - "Test 2, overlay should be blank." - ); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - plugin.style.width = "300px"; - }); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - !overlay || overlay.getAttribute("sizing") == "blank", - "Test 3, overlay should be blank." - ); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - plugin.style.height = "300px"; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - content.document.getElementById("test").clientTop; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && overlay.getAttribute("sizing") != "blank", - "Test 4, overlay should be visible." - ); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - plugin.style.width = "10px"; - plugin.style.height = "10px"; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - content.document.getElementById("test").clientTop; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - !overlay || overlay.getAttribute("sizing") == "blank", - "Test 5, overlay should be blank." - ); - }); -}); - -add_task(async function() { - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - plugin.style.height = "300px"; - plugin.style.width = "300px"; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - content.document.getElementById("test").clientTop; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok( - overlay && overlay.getAttribute("sizing") != "blank", - "Test 6, overlay should be visible." - ); - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_shouldShowOverlay.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -/* 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/. */ - -"use strict"; - -/* This test ensures that the click-to-play "Activate Plugin" overlay - * is shown when expected. - * All testcases are in the plugin_shouldShowOverlay.html file. - * - * Note: Technically, the overlay is *always* shown. When this test was - * originally written, the meaning of "shown" was "shown with the contents", - * as opposed to "shown as blank". The behavior hasn't changed, but the naming - * has: now, a "shown as blank" overlay no longer receives a ".hidden" class. - * It receives a sizing="blank" attribute. - */ - -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); - -var gTestBrowser = null; - -add_task(async function() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - gBrowser.removeCurrentTab(); - gTestBrowser = null; - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "Sanity check, should not have a click-to-play notification" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_shouldShowOverlay.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let testcases = doc.querySelectorAll(".testcase"); - - for (let testcase of testcases) { - let plugin = testcase.querySelector("object"); - Assert.ok(plugin, `plugin exists in ${testcase.id}`); - - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok(overlay, `overlay exists in ${testcase.id}`); - - let expectedVisibility = testcase.getAttribute("shouldshow") == "true"; - Assert.ok( - (overlay.getAttribute("sizing") != "blank") == expectedVisibility, - `The expected visibility is correct in ${testcase.id}` - ); - } - }); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_zoom.js firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_zoom.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_CTP_zoom.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_CTP_zoom.js 2021-01-18 19:58:08.000000000 +0000 @@ -11,11 +11,6 @@ add_task(async function() { registerCleanupFunction(function() { clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); Services.prefs.clearUserPref("extensions.blocklist.suppressUI"); FullZoom.reset(); // must be called before closing the tab we zoomed! gBrowser.removeCurrentTab(); @@ -30,17 +25,6 @@ gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); gTestBrowser = gBrowser.selectedBrowser; - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY, "Test Plug-in"); - - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok( - !popupNotification, - "Test 1, Should not have a click-to-play notification" - ); - await promiseTabLoadEvent( gBrowser.selectedTab, gTestRoot + "plugin_zoom.html" @@ -48,8 +32,6 @@ // Work around for delayed PluginBindingAttached await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); }); // Enlarges the zoom level 4 times and tests that the overlay is @@ -69,8 +51,10 @@ let plugin = doc.getElementById("test"); let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); Assert.ok( - overlay && overlay.classList.contains("visible"), - "Overlay should be visible for zoom change count " + args.count + overlay && + !overlay.classList.contains("visible") && + overlay.getAttribute("blockall") == "blockall", + "Overlay should be present for zoom change count " + args.count ); }); } diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser.ini firefox-85.0+build1/browser/base/content/test/plugins/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -3,95 +3,23 @@ plugin.load_flash_only=false support-files = BlocklistTestProxy.jsm - blockNoPlugins-plugins.json - blockPluginHard-plugins.json - blockPluginInfoURL-plugins.json - blockPluginVulnerableNoUpdate-plugins.json - blockPluginVulnerableUpdatable-plugins.json - browser_clearplugindata.html - browser_clearplugindata_noage.html empty_file.html head.js - plugin_add_dynamically.html - plugin_alternate_content.html - plugin_big.html - plugin_both.html - plugin_both2.html - plugin_bug744745.html - plugin_bug749455.html - plugin_bug787619.html plugin_bug797677.html plugin_favorfallback.html - plugin_hidden_to_visible.html - plugin_iframe.html plugin_outsideScrollArea.html - plugin_overlay_styles.html plugin_simple_blank.swf - plugin_shouldShowOverlay.html - plugin_small.html - plugin_small_2.html - plugin_syncRemoved.html plugin_test.html - plugin_test2.html - plugin_two_types.html - plugin_unknown.html - plugin_crashCommentAndURL.html plugin_zoom.html -[browser_bug743421.js] -tags = blocklist -[browser_bug744745.js] -[browser_bug787619.js] [browser_bug797677.js] -[browser_bug812562.js] -tags = blocklist -[browser_bug818118.js] -[browser_clearplugindata.js] -tags = blocklist -[browser_CTP_context_menu.js] -skip-if = fission || toolkit == "gtk" # Fails with Fission, and we're unlikely to spend time to fix it. fails intermittently on Linux (bug 909342) -tags = blocklist -[browser_CTP_crashreporting.js] -skip-if = !crashreporter || verify || os == 'win' # bug 1442837 -tags = blocklist -[browser_CTP_drag_drop.js] -tags = blocklist [browser_CTP_favorfallback.js] -[browser_CTP_hide_overlay.js] -tags = blocklist -[browser_CTP_iframe.js] -tags = blocklist -[browser_CTP_nonplugins.js] -skip-if = verify -tags = blocklist [browser_CTP_outsideScrollArea.js] tags = blocklist -[browser_CTP_overlay_styles.js] -[browser_CTP_resize.js] -tags = blocklist -[browser_CTP_shouldShowOverlay.js] [browser_CTP_zoom.js] tags = blocklist -[browser_blocking.js] -tags = blocklist -[browser_iterate_hidden_plugins.js] -[browser_plugin_framed_domain.js] -[browser_pluginnotification.js] -tags = blocklist -[browser_plugin_reloading.js] -tags = blocklist -[browser_blocklist_content.js] -skip-if = !e10s -tags = blocklist [browser_enable_DRM_prompt.js] skip-if = (os == 'win' && processor == 'aarch64') # bug 1533164 [browser_private_browsing_eme_persistent_state.js] [browser_globalplugin_crashinfobar.js] skip-if = !crashreporter -[browser_pluginCrashCommentAndURL.js] -skip-if = fission || !crashreporter # Fails with Fission, and we're unlikely to spend time to fix it. -[browser_pluginCrashReportNonDeterminism.js] -skip-if = !crashreporter -[browser_private_clicktoplay.js] -[browser_subframe_access_hidden_plugins.js] -skip-if = fission # Fails with Fission, and we're unlikely to spend time to fix it. diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js firefox-85.0+build1/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_iterate_hidden_plugins.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -"use strict"; - -const TEST_PLUGIN_NAME = "Test Plug-in"; -const HIDDEN_CTP_PLUGIN_PREF = "plugins.navigator.hidden_ctp_plugin"; - -/** - * If a plugin is click-to-play and named in HIDDEN_CTP_PLUGIN_PREF, - * then the plugin should be hidden in the navigator.plugins list by default - * when iterating. - */ - -add_task(async function setup() { - // We'll make the Test Plugin click-to-play. - let originalPluginState = getTestPluginEnabledState(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - registerCleanupFunction(() => { - setTestPluginEnabledState(originalPluginState); - }); - - // And then make the plugin hidden. - await SpecialPowers.pushPrefEnv({ - set: [ - [HIDDEN_CTP_PLUGIN_PREF, TEST_PLUGIN_NAME], - ["plugins.show_infobar", true], - ], - }); -}); - -/** - * Tests that if a plugin is click-to-play and in the - * HIDDEN_CTP_PLUGIN_PREF list, then it shouldn't be visible - * when iterating navigator.plugins. - */ -add_task(async function test_plugin_is_hidden_on_iteration() { - // The plugin should not be visible when we iterate - // navigator.plugins. - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "http://example.com", - }, - async function(browser) { - await SpecialPowers.spawn(browser, [TEST_PLUGIN_NAME], async function( - pluginName - ) { - let plugins = Array.from(content.navigator.plugins); - Assert.ok( - plugins.every(p => p.name != pluginName), - "Should not find Test Plugin" - ); - }); - } - ); - - // Now clear the HIDDEN_CTP_PLUGIN_PREF temporarily and - // make sure we can see the plugin again. - await SpecialPowers.pushPrefEnv({ - set: [[HIDDEN_CTP_PLUGIN_PREF, ""]], - }); - - // Note that I have to do this in a new tab since navigator - // caches navigator.plugins after an initial read. - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: "http://example.com", - }, - async function(browser) { - await SpecialPowers.spawn(browser, [TEST_PLUGIN_NAME], async function( - pluginName - ) { - let plugins = Array.from(content.navigator.plugins); - Assert.ok( - plugins.some(p => p.name == pluginName), - "Should have found the Test Plugin" - ); - }); - } - ); - - await SpecialPowers.popPrefEnv(); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginCrashCommentAndURL.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,315 +0,0 @@ -/* global gBrowser */ -ChromeUtils.import("resource://gre/modules/CrashSubmit.jsm", this); - -const SERVER_URL = - "http://example.com/browser/toolkit/crashreporter/test/browser/crashreport.sjs"; - -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gTestBrowser = null; -var config = {}; - -add_task(async function() { - // The test harness sets MOZ_CRASHREPORTER_NO_REPORT, which disables plugin - // crash reports. This test needs them enabled. The test also needs a mock - // report server, and fortunately one is already set up by toolkit/ - // crashreporter/test/Makefile.in. Assign its URL to MOZ_CRASHREPORTER_URL, - // which CrashSubmit.jsm uses as a server override. - let env = Cc["@mozilla.org/process/environment;1"].getService( - Ci.nsIEnvironment - ); - let noReport = env.get("MOZ_CRASHREPORTER_NO_REPORT"); - let serverUrl = env.get("MOZ_CRASHREPORTER_URL"); - env.set("MOZ_CRASHREPORTER_NO_REPORT", ""); - env.set("MOZ_CRASHREPORTER_URL", SERVER_URL); - - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - // Crash immediately - Services.prefs.setIntPref("dom.ipc.plugins.timeoutSecs", 0); - - registerCleanupFunction(async function() { - Services.prefs.clearUserPref("dom.ipc.plugins.timeoutSecs"); - env.set("MOZ_CRASHREPORTER_NO_REPORT", noReport); - env.set("MOZ_CRASHREPORTER_URL", serverUrl); - env = null; - config = null; - gTestBrowser = null; - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - config = { - shouldSubmissionUIBeVisible: true, - comment: "", - urlOptIn: false, - }; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED); - - let pluginCrashed = promisePluginCrashed(); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_crashCommentAndURL.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - // Wait for the plugin to crash - await pluginCrashed; - - let crashReportStatus = TestUtils.topicObserved( - "crash-report-status", - onSubmitStatus - ); - - // Test that the crash submission UI is actually visible and submit the crash report. - await SpecialPowers.spawn(gTestBrowser, [config], async function(aConfig) { - let doc = content.document; - let plugin = doc.getElementById("plugin"); - let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById( - "pleaseSubmit" - ); - let submitButton = plugin.openOrClosedShadowRoot.getElementById( - "submitButton" - ); - // Test that we don't send the URL when urlOptIn is false. - plugin.openOrClosedShadowRoot.getElementById("submitURLOptIn").checked = - aConfig.urlOptIn; - submitButton.click(); - Assert.equal( - content.getComputedStyle(pleaseSubmit).display == "block", - aConfig.shouldSubmissionUIBeVisible, - "The crash UI should be visible" - ); - }); - - await crashReportStatus; -}); - -add_task(async function() { - config = { - shouldSubmissionUIBeVisible: true, - comment: "a test comment", - urlOptIn: true, - }; - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED); - - let pluginCrashed = promisePluginCrashed(); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_crashCommentAndURL.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - // Wait for the plugin to crash - await pluginCrashed; - - let crashReportStatus = TestUtils.topicObserved( - "crash-report-status", - onSubmitStatus - ); - - // Test that the crash submission UI is actually visible and submit the crash report. - await SpecialPowers.spawn(gTestBrowser, [config], async function(aConfig) { - let doc = content.document; - let plugin = doc.getElementById("plugin"); - let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById( - "pleaseSubmit" - ); - let submitButton = plugin.openOrClosedShadowRoot.getElementById( - "submitButton" - ); - // Test that we send the URL when urlOptIn is true. - plugin.openOrClosedShadowRoot.getElementById("submitURLOptIn").checked = - aConfig.urlOptIn; - plugin.openOrClosedShadowRoot.getElementById("submitComment").value = - aConfig.comment; - submitButton.click(); - Assert.equal( - content.getComputedStyle(pleaseSubmit).display == "block", - aConfig.shouldSubmissionUIBeVisible, - "The crash UI should be visible" - ); - }); - - await crashReportStatus; -}); - -add_task(async function() { - config = { - shouldSubmissionUIBeVisible: false, - comment: "", - urlOptIn: true, - }; - - // Deferred promise object used by the test to wait for the crash handler - let crashDeferred = PromiseUtils.defer(); - - // Clear out any minidumps we create from plugin crashes, this is needed - // because we do not submit the crash otherwise the submission process would - // have deleted the crash dump files for us. - let crashObserver = (subject, topic, data) => { - if (topic != "plugin-crashed") { - return; - } - - let propBag = subject.QueryInterface(Ci.nsIPropertyBag2); - let minidumpID = propBag.getPropertyAsAString("pluginDumpID"); - let additionalDumps = propBag.getPropertyAsACString("additionalMinidumps"); - - Services.crashmanager.ensureCrashIsPresent(minidumpID).then(() => { - let minidumpDir = Services.dirsvc.get("UAppData", Ci.nsIFile); - minidumpDir.append("Crash Reports"); - minidumpDir.append("pending"); - - let pluginDumpFile = minidumpDir.clone(); - pluginDumpFile.append(minidumpID + ".dmp"); - - let extraFile = minidumpDir.clone(); - extraFile.append(minidumpID + ".extra"); - - pluginDumpFile.remove(false); - extraFile.remove(false); - - if (additionalDumps.length) { - const names = additionalDumps.split(","); - for (const name of names) { - let additionalDumpFile = minidumpDir.clone(); - additionalDumpFile.append(minidumpID + "-" + name + ".dmp"); - additionalDumpFile.remove(false); - } - } - - crashDeferred.resolve(); - }); - }; - - Services.obs.addObserver(crashObserver, "plugin-crashed"); - - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED); - - let pluginCrashed = promisePluginCrashed(); - - // Make sure that the plugin container is too small to display the crash submission UI - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + - "plugin_crashCommentAndURL.html?" + - encodeURIComponent(JSON.stringify({ width: 300, height: 300 })) - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - // Wait for the plugin to crash - await pluginCrashed; - - // Test that the crash submission UI is not visible and do not submit a crash report. - await SpecialPowers.spawn(gTestBrowser, [config], async function(aConfig) { - let doc = content.document; - let plugin = doc.getElementById("plugin"); - let pleaseSubmit = plugin.openOrClosedShadowRoot.getElementById( - "pleaseSubmit" - ); - Assert.equal( - !!pleaseSubmit && - content.getComputedStyle(pleaseSubmit).display == "block", - aConfig.shouldSubmissionUIBeVisible, - "Plugin crash UI should not be visible" - ); - }); - - await crashDeferred.promise; - Services.obs.removeObserver(crashObserver, "plugin-crashed"); -}); - -function promisePluginCrashed() { - return new ContentTask.spawn(gTestBrowser, {}, async function() { - await new Promise(resolve => { - addEventListener( - "PluginCrashReporterDisplayed", - function onPluginCrashed() { - removeEventListener("PluginCrashReporterDisplayed", onPluginCrashed); - resolve(); - } - ); - }); - }); -} - -function onSubmitStatus(aSubject, aData) { - if (aData === "submitting") { - return false; - } - - is(aData, "success", "The crash report should be submitted successfully"); - - let propBag = aSubject.QueryInterface(Ci.nsIPropertyBag); - if (aData == "success") { - let remoteID = getPropertyBagValue(propBag, "serverCrashID"); - ok(!!remoteID, "serverCrashID should be set"); - - // Remove the submitted report file. - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - file.initWithPath(Services.crashmanager._submittedDumpsDir); - file.append(remoteID + ".txt"); - ok(file.exists(), "Submitted report file should exist"); - file.remove(false); - } - - let extra = getPropertyBagValue(propBag, "extra"); - ok(extra instanceof Ci.nsIPropertyBag, "Extra data should be property bag"); - - let val = getPropertyBagValue(extra, "PluginUserComment"); - if (config.comment) { - is( - val, - config.comment, - "Comment in extra data should match comment in textbox" - ); - } else { - ok( - val === undefined, - "Comment should be absent from extra data when textbox is empty" - ); - } - - val = getPropertyBagValue(extra, "PluginContentURL"); - if (config.urlOptIn) { - is( - val, - gBrowser.currentURI.spec, - "URL in extra data should match browser URL when opt-in checked" - ); - } else { - ok( - val === undefined, - "URL should be absent from extra data when opt-in not checked" - ); - } - - return true; -} - -function getPropertyBagValue(bag, key) { - try { - var val = bag.getProperty(key); - } catch (e) { - if (e.result != Cr.NS_ERROR_FAILURE) { - throw e; - } - } - return val; -} diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginCrashReportNonDeterminism.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,321 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ */ - -const { PromiseUtils } = ChromeUtils.import( - "resource://gre/modules/PromiseUtils.jsm" -); - -const { PluginManager } = ChromeUtils.import( - "resource:///actors/PluginParent.jsm" -); - -/** - * With e10s, plugins must run in their own process. This means we have - * three processes at a minimum when we're running a plugin: - * - * 1) The main browser, or "chrome" process - * 2) The content process hosting the plugin instance - * 3) The plugin process - * - * If the plugin process crashes, we cannot be sure if the chrome process - * will hear about it first, or the content process will hear about it - * first. Because of how IPC works, that's really up to the operating system, - * and we assume any guarantees about it, so we have to account for both - * possibilities. - * - * This test exercises the browser's reaction to both possibilities. - */ - -const CRASH_URL = - "http://example.com/browser/browser/base/content/test/plugins/plugin_crashCommentAndURL.html"; - -/** - * In order for our test to work, we need to be able to put a plugin - * in a very specific state. Specifically, we need it to match the - * :-moz-handler-crashed pseudoselector. The only way I can find to - * do that is by actually crashing the plugin. So we wait for the - * plugin to crash and show the "please" state (since that will - * only show if both the message from the parent has been received - * AND the PluginCrashed event has fired). - * - * Once in that state, we try to rewind the clock a little bit - we clear - * out the crashData cache in the PluginContent with a message, and we also - * override the pluginFallbackState of the to fool PluginContent - * into believing that the plugin is in a particular state. - * - * @param browser - * The browser that has loaded the CRASH_URL that we need to - * prepare to be in the special state. - * @param pluginFallbackState - * The value we should override the 's pluginFallbackState - * with. - * @return Promise - * The Promise resolves when the plugin has officially been put into - * the crash reporter state, and then "rewound" to have the "status" - * attribute of the statusDiv removed. The resolved Promise returns - * the run ID for the crashed plugin. It rejects if we never get into - * the crash reporter state. - */ -function preparePlugin(browser, pluginFallbackState) { - return SpecialPowers.spawn(browser, [pluginFallbackState], async function( - contentPluginFallbackState - ) { - let plugin = content.document.getElementById("plugin"); - // CRASH_URL will load a plugin that crashes immediately. We - // wait until the plugin has finished being put into the crash - // state. - let statusDiv; - await ContentTaskUtils.waitForCondition(() => { - statusDiv = plugin.openOrClosedShadowRoot.getElementById("submitStatus"); - - return statusDiv && statusDiv.getAttribute("status") == "please"; - }, "Timed out waiting for plugin to be in crash report state"); - - // "Rewind", by wiping out the status attribute... - statusDiv.removeAttribute("status"); - // Somehow, I'm able to get away with overriding the getter for - // this XPCOM object. Probably because I've got chrome privledges. - Object.defineProperty(plugin, "pluginFallbackType", { - get() { - return contentPluginFallbackState; - }, - }); - return plugin.runID; - }).then(runID => { - let { currentWindowGlobal } = browser.frameLoader.browsingContext; - currentWindowGlobal - .getActor("Plugin") - .sendAsyncMessage("PluginParent:Test:ClearCrashData"); - return runID; - }); -} - -// Bypass click-to-play -setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED); - -// Deferred promise object used by the test to wait for the crash handler -let crashDeferred = null; - -// Clear out any minidumps we create from plugins - we really don't care -// about them. -let crashObserver = (subject, topic, data) => { - if (topic != "plugin-crashed") { - return; - } - - let propBag = subject.QueryInterface(Ci.nsIPropertyBag2); - let minidumpID = propBag.getPropertyAsAString("pluginDumpID"); - let additionalMinidumps = propBag.getPropertyAsACString( - "additionalMinidumps" - ); - - Services.crashmanager.ensureCrashIsPresent(minidumpID).then(() => { - let minidumpDir = Services.dirsvc.get("ProfD", Ci.nsIFile); - minidumpDir.append("minidumps"); - - let pluginDumpFile = minidumpDir.clone(); - pluginDumpFile.append(minidumpID + ".dmp"); - - let extraFile = minidumpDir.clone(); - extraFile.append(minidumpID + ".extra"); - - ok(pluginDumpFile.exists(), "Found minidump"); - ok(extraFile.exists(), "Found extra file"); - - pluginDumpFile.remove(false); - extraFile.remove(false); - - if (additionalMinidumps.length) { - const names = additionalMinidumps.split(","); - for (const name of names) { - let additionalDumpFile = minidumpDir.clone(); - additionalDumpFile.append(minidumpID + "-" + name + ".dmp"); - additionalDumpFile.remove(false); - } - } - - crashDeferred.resolve(); - }); -}; - -Services.obs.addObserver(crashObserver, "plugin-crashed"); -// plugins.testmode will make PluginParent:Test:ClearCrashData work. -Services.prefs.setBoolPref("plugins.testmode", true); -registerCleanupFunction(() => { - Services.prefs.clearUserPref("plugins.testmode"); - Services.obs.removeObserver(crashObserver, "plugin-crashed"); -}); - -/** - * In this case, the chrome process hears about the crash first. - */ -add_task(async function testChromeHearsPluginCrashFirst() { - // Setup the crash observer promise - crashDeferred = PromiseUtils.defer(); - - // Open a remote window so that we can run this test even if e10s is not - // enabled by default. - let win = await BrowserTestUtils.openNewBrowserWindow({ remote: true }); - let browser = win.gBrowser.selectedBrowser; - - BrowserTestUtils.loadURI(browser, CRASH_URL); - await BrowserTestUtils.browserLoaded(browser); - - // In this case, we want the to match the -moz-handler-crashed - // pseudoselector, but we want it to seem still active, because the - // content process is not yet supposed to know that the plugin has - // crashed. - await preparePlugin(browser, Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE); - - // In this case, the parent responds immediately when the child asks - // for crash data. in `testContentHearsCrashFirst we will delay the - // response (simulating what happens if the parent doesn't know about - // the crash yet). - await SpecialPowers.spawn(browser, [], async function() { - // At this point, the content process should have heard the - // plugin crash message from the parent, and we are OK to emit - // the PluginCrashed event. - let plugin = content.document.getElementById("plugin"); - let statusDiv = plugin.openOrClosedShadowRoot.getElementById( - "submitStatus" - ); - - if (statusDiv.getAttribute("status") == "please") { - Assert.ok(false, "Did not expect plugin to be in crash report mode yet."); - return; - } - - // Now we need the plugin to seem crashed to the child actor, without - // actually crashing the plugin again. We hack around this by overriding - // the pluginFallbackType again. - Object.defineProperty(plugin, "pluginFallbackType", { - get() { - return Ci.nsIObjectLoadingContent.PLUGIN_CRASHED; - }, - }); - - let event = new content.PluginCrashedEvent("PluginCrashed", { - pluginName: "", - pluginDumpID: "", - submittedCrashReport: false, - bubbles: true, - cancelable: true, - }); - - plugin.dispatchEvent(event); - // The plugin child actor will go fetch crash info in the parent. Wait - // for it to come back: - await ContentTaskUtils.waitForCondition( - () => statusDiv.getAttribute("status") == "please" - ); - Assert.equal( - statusDiv.getAttribute("status"), - "please", - "Should have been showing crash report UI" - ); - }); - await BrowserTestUtils.closeWindow(win); - await crashDeferred.promise; -}); - -/** - * In this case, the content process hears about the crash first. - */ -add_task(async function testContentHearsCrashFirst() { - // Setup the crash observer promise - crashDeferred = PromiseUtils.defer(); - - // Open a remote window so that we can run this test even if e10s is not - // enabled by default. - let win = await BrowserTestUtils.openNewBrowserWindow({ remote: true }); - let browser = win.gBrowser.selectedBrowser; - - BrowserTestUtils.loadURI(browser, CRASH_URL); - await BrowserTestUtils.browserLoaded(browser); - - // In this case, we want the to match the -moz-handler-crashed - // pseudoselector, and we want the plugin to seem crashed, since the - // content process in this case has heard about the crash first. - let runID = await preparePlugin( - browser, - Ci.nsIObjectLoadingContent.PLUGIN_CRASHED - ); - - // We resolve this promise when we're ready to tell the child from the parent. - let allowParentToRespond = PromiseUtils.defer(); - // This promise is resolved as soon as we're contacted by the child. - // It forces the parent not to respond until `allowParentToRespond` has been - // resolved. - let parentRequestPromise = new Promise(resolve => { - PluginManager.mockResponse(browser, function(data) { - resolve(data); - - return allowParentToRespond.promise.then(() => { - return { pluginName: "", runID, state: "please" }; - }); - }); - }); - - await SpecialPowers.spawn(browser, [], async function() { - // At this point, the content process has not yet heard from the - // parent about the crash report. Let's ensure that by making sure - // we're not showing the plugin crash report UI. - let plugin = content.document.getElementById("plugin"); - let statusDiv = plugin.openOrClosedShadowRoot.getElementById( - "submitStatus" - ); - - if (statusDiv.getAttribute("status") == "please") { - Assert.ok(false, "Did not expect plugin to be in crash report mode yet."); - } - - let event = new content.PluginCrashedEvent("PluginCrashed", { - pluginName: "", - pluginDumpID: "", - submittedCrashReport: false, - bubbles: true, - cancelable: true, - }); - - plugin.dispatchEvent(event); - }); - let receivedData = await parentRequestPromise; - is(receivedData.runID, runID, "Should get a request for the same crash."); - - await SpecialPowers.spawn(browser, [], function() { - let plugin = content.document.getElementById("plugin"); - let statusDiv = plugin.openOrClosedShadowRoot.getElementById( - "submitStatus" - ); - Assert.notEqual( - statusDiv.getAttribute("status"), - "please", - "Should not yet be showing crash report UI" - ); - }); - - // Now allow the parent to respond to the child with crash info: - allowParentToRespond.resolve(); - - await SpecialPowers.spawn(browser, [], async function() { - // At this point, the content process will have heard the message - // from the parent and reacted to it. We should be showing the plugin - // crash report UI now. - let plugin = content.document.getElementById("plugin"); - let statusDiv = plugin.openOrClosedShadowRoot.getElementById( - "submitStatus" - ); - await ContentTaskUtils.waitForCondition(() => { - return statusDiv && statusDiv.getAttribute("status") == "please"; - }); - - Assert.equal( - statusDiv.getAttribute("status"), - "please", - "Should have been showing crash report UI" - ); - }); - - await BrowserTestUtils.closeWindow(win); - await crashDeferred.promise; -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_plugin_framed_domain.js firefox-85.0+build1/browser/base/content/test/plugins/browser_plugin_framed_domain.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_plugin_framed_domain.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_plugin_framed_domain.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - -"use strict"; - -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "https://example.com/" -); - -/** - * Verify that giving permission to a plugin works based on the toplevel - * page's principal, so that permissions meant for framed plugins persist - * correctly for the duration of the session. - */ -add_task(async function test_toplevel_frame_permission() { - await BrowserTestUtils.withNewTab( - gTestRoot + "empty_file.html", - async browser => { - // Add a cross-origin iframe and return when it's loaded. - await SpecialPowers.spawn(browser.browsingContext, [], async function() { - let doc = content.document; - let iframe = doc.createElement("iframe"); - let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load"); - iframe.src = doc.location.href.replace(".com/", ".org/"); - doc.body.appendChild(iframe); - // Note that we cannot return (rather than await) loadPromise, because - // it resolves with the event, which isn't structured-clonable. - await loadPromise; - }); - - // Show a plugin notification from the iframe's actor: - let { currentWindowGlobal } = browser.browsingContext.children[0]; - let actor = currentWindowGlobal.getActor("Plugin"); - const kHost = Cc["@mozilla.org/plugin/host;1"].getService( - Ci.nsIPluginHost - ); - const { PLUGIN_CLICK_TO_PLAY } = Ci.nsIObjectLoadingContent; - let plugin = kHost.getPluginTags()[0]; - actor.showClickToPlayNotification( - browser, - { id: plugin.id, fallbackType: PLUGIN_CLICK_TO_PLAY }, - false /* showNow */ - ); - - // Check that it is associated with the toplevel origin (.com), not - // the subframe's origin (.org): - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - browser - ); - is( - notification.options.principal.host, - "example.com", - "Should use top host for permission prompt!" - ); - } - ); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginnotification.js firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginnotification.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_pluginnotification.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_pluginnotification.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,626 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); -var gTestBrowser = null; - -function updateAllTestPlugins(aState) { - setTestPluginEnabledState(aState, "Test Plug-in"); - setTestPluginEnabledState(aState, "Second Test Plug-in"); -} - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gTestBrowser = null; - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - // Prime the blocklist service, the remote service doesn't launch on startup. - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); -}); - -// Tests a page with an unknown plugin in it. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_unknown.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("unknown"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED, - "Test 1a, plugin fallback type should be PLUGIN_UNSUPPORTED" - ); -}); - -// Test that the doorhanger is shown when the user clicks on the overlay -// after having previously blocked the plugin. -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Plugin should not be activated"); - - // Simulate clicking the "Allow" button. - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Plugin should be activated"); - - // Simulate clicking the "Block" button. - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.secondaryButton.click(); - - pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Plugin should not be activated"); - - let browserLoaded = BrowserTestUtils.browserLoaded(gTestBrowser); - gTestBrowser.reload(); - await browserLoaded; - notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - - // Simulate clicking the overlay - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let bounds = plugin.openOrClosedShadowRoot - .getElementById("main") - .getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - - ok(!notification.dismissed, "A plugin notification should be shown."); - - clearAllPluginPermissions(); -}); - -// Tests that going back will reshow the notification for click-to-play plugins -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,hi" - ); - - // make sure the notification is gone - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(!notification, "Test 11b, Should not have a click-to-play notification"); - - gTestBrowser.webNavigation.goBack(); - - await promisePopupNotification("click-to-play-plugins"); -}); - -// Tests that the "Allow Always" permission works for click-to-play plugins -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 12a, Plugin should not be activated"); - - // Simulate clicking the "Allow" button. - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - - await promiseForNotificationShown(notification); - - PopupNotifications.panel.firstElementChild.button.click(); - - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 12a, Plugin should be activated"); -}); - -// Test that the "Always" permission, when set for just the Test plugin, -// does not also allow the Second Test plugin. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_two_types.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - await promisePopupNotification("click-to-play-plugins"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let test = content.document.getElementById("test"); - let secondtestA = content.document.getElementById("secondtestA"); - let secondtestB = content.document.getElementById("secondtestB"); - Assert.ok( - test.activated && !secondtestA.activated && !secondtestB.activated, - "Content plugins are set up" - ); - }); - - clearAllPluginPermissions(); -}); - -// Tests that the plugin's "activated" property is true for working plugins -// with click-to-play disabled. -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_ENABLED); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test2.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("test1"); - ok(pluginInfo.activated, "Test 14, Plugin should be activated"); -}); - -// Tests that the overlay is shown instead of alternate content when -// plugins are click to play. -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_alternate_content.html" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let mainBox = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok(!!mainBox, "Test 15, Plugin overlay should exist"); - }); -}); - -// Tests that mContentType is used for click-to-play plugins, and not the -// inspected type. -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_bug749455.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 17, Should have a click-to-play notification"); -}); - -// Tests that clicking the icon of the overlay activates the doorhanger -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated"); - - ok( - PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed, - "Test 19a, Doorhanger should start out dismissed" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let icon = plugin.openOrClosedShadowRoot.getElementById("icon"); - let bounds = icon.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - - let condition = () => - !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed; - await promiseForCondition(condition); -}); - -// Tests that clicking the text of the overlay activates the plugin -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated"); - - ok( - PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed, - "Test 19c, Doorhanger should start out dismissed" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let text = plugin.openOrClosedShadowRoot.getElementById("clickToPlay"); - let bounds = text.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - - let condition = () => - !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed; - await promiseForCondition(condition); -}); - -// Tests that clicking the box of the overlay activates the doorhanger -// (just to be thorough) -add_task(async function() { - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 18g, Plugin should not be activated"); - - ok( - PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed, - "Test 19e, Doorhanger should start out dismissed" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", 50, 50, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", 50, 50, 0, 1, 0, false, 0, 0); - }); - - let condition = () => - !PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser) - .dismissed && PopupNotifications.panel.firstElementChild; - await promiseForCondition(condition); - PopupNotifications.panel.firstElementChild.button.click(); - - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 19e, Plugin should not be activated"); - - clearAllPluginPermissions(); -}); - -// Tests that a plugin in a div that goes from style="display: none" to -// "display: block" can be clicked to activate. -add_task(async function() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_hidden_to_visible.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 20a, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let overlay = plugin.openOrClosedShadowRoot.getElementById("main"); - Assert.ok(!!overlay, "Test 20a, Plugin overlay should exist"); - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let mainBox = plugin.openOrClosedShadowRoot.getElementById("main"); - let overlayRect = mainBox.getBoundingClientRect(); - Assert.ok( - overlayRect.width == 0 && overlayRect.height == 0, - "Test 20a, plugin should have an overlay with 0px width and height" - ); - }); - - let pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 20b, plugin should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let div = doc.getElementById("container"); - Assert.equal( - div.style.display, - "none", - "Test 20b, container div should be display: none" - ); - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let div = doc.getElementById("container"); - div.style.display = "block"; - }); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - // Waiting for layout to flush and the overlay layout to compute - await new Promise(resolve => content.requestAnimationFrame(resolve)); - await new Promise(resolve => content.requestAnimationFrame(resolve)); - - let doc = content.document; - let plugin = doc.getElementById("test"); - let mainBox = plugin.openOrClosedShadowRoot.getElementById("main"); - let overlayRect = mainBox.getBoundingClientRect(); - Assert.ok( - overlayRect.width == 200 && overlayRect.height == 200, - "Test 20c, plugin should have overlay dims of 200px" - ); - }); - - pluginInfo = await promiseForPluginInfo("test"); - ok(!pluginInfo.activated, "Test 20b, plugin should not be activated"); - - ok(notification.dismissed, "Test 20c, Doorhanger should start out dismissed"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - let bounds = plugin.getBoundingClientRect(); - let left = (bounds.left + bounds.right) / 2; - let top = (bounds.top + bounds.bottom) / 2; - let utils = content.windowUtils; - utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0); - utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0); - }); - - let condition = () => - !notification.dismissed && !!PopupNotifications.panel.firstElementChild; - await promiseForCondition(condition); - PopupNotifications.panel.firstElementChild.button.click(); - - pluginInfo = await promiseForPluginInfo("test"); - ok(pluginInfo.activated, "Test 20c, plugin should be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - Assert.ok( - !plugin.openOrClosedShadowRoot, - "Test 20c, CTP UA Widget Shadow Root is removed" - ); - }); - - clearAllPluginPermissions(); -}); - -// Tests that a click-to-play plugin resets its activated state when changing types -add_task(async function() { - clearAllPluginPermissions(); - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 22, Should have a click-to-play notification"); - - // Plugin should start as CTP - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "Test 23, plugin fallback type should be PLUGIN_CLICK_TO_PLAY" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - plugin.type = null; - // We currently don't properly change state just on type change, - // so rebind the plugin to tree. bug 767631 - plugin.parentNode.appendChild(plugin); - }); - - pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.displayedType, - Ci.nsIObjectLoadingContent.TYPE_NULL, - "Test 23, plugin should be TYPE_NULL" - ); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let doc = content.document; - let plugin = doc.getElementById("test"); - plugin.type = "application/x-test"; - plugin.parentNode.appendChild(plugin); - }); - - pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.displayedType, - Ci.nsIObjectLoadingContent.TYPE_NULL, - "Test 23, plugin should be TYPE_NULL" - ); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "Test 23, plugin fallback type should be PLUGIN_CLICK_TO_PLAY" - ); - ok(!pluginInfo.activated, "Test 23, plugin node should not be activated"); -}); - -// Plugin sync removal test. Note this test produces a notification drop down since -// the plugin we add has zero dims. -add_task(async function blockPluginSyncRemoved() { - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_syncRemoved.html" - ); - - // Maybe there some better trick here, we need to wait for the page load, then - // wait for the js to execute in the page. - await waitForMs(500); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins" - ); - ok( - notification, - "Test 25: There should be a plugin notification even if the plugin was immediately removed" - ); - ok( - notification.dismissed, - "Test 25: The notification should be dismissed by default" - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html,hi" - ); -}); - -// Tests a page with a blocked plugin in it and make sure the infoURL property -// the blocklist file gets used. -add_task(async function blockPluginInfoURL() { - clearAllPluginPermissions(); - - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockPluginInfoURL", - gTestBrowser - ); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - info("Waiting for plugin bindings"); - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins" - ); - - info("Waiting for notification to be shown"); - // Since the plugin notification is dismissed by default, reshow it. - await promiseForNotificationShown(notification); - - info("Waiting for plugin info"); - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED, - "Test 26, plugin fallback type should be PLUGIN_BLOCKLISTED" - ); - ok(!pluginInfo.activated, "Plugin should be activated."); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_plugin_reloading.js firefox-85.0+build1/browser/base/content/test/plugins/browser_plugin_reloading.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_plugin_reloading.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_plugin_reloading.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -var gTestRoot = getRootDirectory(gTestPath).replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); -var gTestBrowser = null; - -function updateAllTestPlugins(aState) { - setTestPluginEnabledState(aState, "Test Plug-in"); - setTestPluginEnabledState(aState, "Second Test Plug-in"); -} - -add_task(async function() { - registerCleanupFunction(async function() { - clearAllPluginPermissions(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in"); - setTestPluginEnabledState( - Ci.nsIPluginTag.STATE_ENABLED, - "Second Test Plug-in" - ); - await asyncSetAndUpdateBlocklist( - gTestRoot + "blockNoPlugins", - gTestBrowser - ); - gTestBrowser = null; - gBrowser.removeCurrentTab(); - window.focus(); - }); -}); - -add_task(async function() { - gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser); - gTestBrowser = gBrowser.selectedBrowser; - - Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true); - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - // Prime the blocklist service, the remote service doesn't launch on startup. - await promiseTabLoadEvent( - gBrowser.selectedTab, - "data:text/html," - ); - - await asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins", gTestBrowser); -}); - -// Tests that a click-to-play plugin retains its activated state upon reloading -add_task(async function() { - clearAllPluginPermissions(); - - updateAllTestPlugins(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - - await promiseTabLoadEvent( - gBrowser.selectedTab, - gTestRoot + "plugin_test.html" - ); - - // Work around for delayed PluginBindingAttached - await promiseUpdatePluginBindings(gTestBrowser); - - let notification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(notification, "Test 1, Should have a click-to-play notification"); - - let pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.pluginFallbackType, - Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, - "Test 2, plugin fallback type should be PLUGIN_CLICK_TO_PLAY" - ); - - // run the plugin - await promisePlayObject("test"); - - await promiseUpdatePluginBindings(gTestBrowser); - - pluginInfo = await promiseForPluginInfo("test"); - is( - pluginInfo.displayedType, - Ci.nsIObjectLoadingContent.TYPE_PLUGIN, - "Test 3, plugin should have started" - ); - ok(pluginInfo.activated, "Test 4, plugin node should not be activated"); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - let npobj1 = Cu.waiveXrays(plugin).getObjectValue(); - // eslint-disable-next-line no-self-assign - plugin.src = plugin.src; - let pluginsDiffer = false; - try { - Cu.waiveXrays(plugin).checkObjectValue(npobj1); - } catch (e) { - pluginsDiffer = true; - } - - Assert.ok(pluginsDiffer, "Test 5, plugins differ."); - }); - - pluginInfo = await promiseForPluginInfo("test"); - ok( - pluginInfo.activated, - "Test 6, Plugin should have retained activated state." - ); - is( - pluginInfo.displayedType, - Ci.nsIObjectLoadingContent.TYPE_PLUGIN, - "Test 7, plugin should have started" - ); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_private_clicktoplay.js firefox-85.0+build1/browser/base/content/test/plugins/browser_private_clicktoplay.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_private_clicktoplay.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_private_clicktoplay.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,268 +0,0 @@ -var rootDir = getRootDirectory(gTestPath); -const gTestRoot = rootDir; -const gHttpTestRoot = rootDir.replace( - "chrome://mochitests/content/", - "http://127.0.0.1:8888/" -); - -var gTestBrowser = null; -var gNextTest = null; -var gPluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); - -var gPrivateWindow = null; -var gPrivateBrowser = null; - -function finishTest() { - clearAllPluginPermissions(); - gBrowser.removeCurrentTab(); - if (gPrivateWindow) { - gPrivateWindow.close(); - } - window.focus(); -} - -let createPrivateWindow = async function createPrivateWindow(url) { - gPrivateWindow = await BrowserTestUtils.openNewBrowserWindow({ - private: true, - }); - ok(!!gPrivateWindow, "should have created a private window."); - gPrivateBrowser = gPrivateWindow.gBrowser.selectedBrowser; - - BrowserTestUtils.loadURI(gPrivateBrowser, url); - await BrowserTestUtils.browserLoaded(gPrivateBrowser, false, url); - info("loaded " + url); -}; - -add_task(async function test() { - registerCleanupFunction(function() { - clearAllPluginPermissions(); - getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_ENABLED; - getTestPlugin("Second Test Plug-in").enabledState = - Ci.nsIPluginTag.STATE_ENABLED; - }); - - // This test works under the assumption that private browsing windows - // share permissions with regular windows. Setting a pref to revert to this - // behavior. The test should be updated and the pref overwrite removed. - // See Bug 1588457 - await SpecialPowers.pushPrefEnv({ - set: [["permissions.isolateBy.privateBrowsing", false]], - }); - - let newTab = BrowserTestUtils.addTab(gBrowser); - gBrowser.selectedTab = newTab; - gTestBrowser = gBrowser.selectedBrowser; - let promise = BrowserTestUtils.browserLoaded(gTestBrowser); - - getTestPlugin().enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY; - getTestPlugin("Second Test Plug-in").enabledState = - Ci.nsIPluginTag.STATE_CLICKTOPLAY; - await promise; -}); - -add_task(async function test1a() { - await createPrivateWindow(gHttpTestRoot + "plugin_test.html"); -}); - -add_task(async function test1b() { - let popupNotification = gPrivateWindow.PopupNotifications.getNotification( - "click-to-play-plugins", - gPrivateBrowser - ); - ok(popupNotification, "Test 1b, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gPrivateBrowser, [], function() { - let plugin = content.document.getElementById("test"); - ok(!plugin.activated, "Test 1b, Plugin should not be activated"); - }); - - // Check the button status - let promiseShown = BrowserTestUtils.waitForEvent( - gPrivateWindow.PopupNotifications.panel, - "Shown" - ); - popupNotification.reshow(); - - await promiseShown; - is( - gPrivateWindow.PopupNotifications.panel.firstElementChild.checkbox.hidden, - true, - "'Remember' checkbox should be hidden in private windows" - ); - - let promises = [ - BrowserTestUtils.browserLoaded( - gTestBrowser, - false, - gHttpTestRoot + "plugin_test.html" - ), - BrowserTestUtils.waitForEvent(window, "activate"), - ]; - gPrivateWindow.close(); - BrowserTestUtils.loadURI(gTestBrowser, gHttpTestRoot + "plugin_test.html"); - await Promise.all(promises); - await SimpleTest.promiseFocus(window); -}); - -add_task(async function test2a() { - // enable test plugin on this site - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(popupNotification, "Test 2a, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], function() { - let plugin = content.document.getElementById("test"); - ok(!plugin.activated, "Test 2a, Plugin should not be activated"); - }); - - // Simulate clicking the "Allow Now" button. - let promiseShown = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "Shown" - ); - popupNotification.reshow(); - await promiseShown; - - PopupNotifications.panel.firstElementChild.button.click(); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - let condition = () => plugin.activated; - await ContentTaskUtils.waitForCondition( - condition, - "Test 2a, Waited too long for plugin to activate" - ); - }); -}); - -add_task(async function test2c() { - let topicObserved = TestUtils.topicObserved( - "PopupNotifications-updateNotShowing" - ); - await createPrivateWindow(gHttpTestRoot + "plugin_test.html"); - await topicObserved; - - let popupNotification = await TestUtils.waitForCondition(() => { - return gPrivateWindow.PopupNotifications.getNotification( - "click-to-play-plugins", - gPrivateBrowser - ); - }, "Waiting for click-to-play-plugins notification in the private window"); - ok(popupNotification, "Test 2c, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gPrivateBrowser, [], function() { - let plugin = content.document.getElementById("test"); - ok(plugin.activated, "Test 2c, Plugin should be activated"); - }); - - // Check the button status - let promiseShown = BrowserTestUtils.waitForEvent( - gPrivateWindow.PopupNotifications.panel, - "Shown" - ); - popupNotification.reshow(); - await promiseShown; - is( - gPrivateWindow.PopupNotifications.panel.firstElementChild.secondaryButton - .hidden, - false, - "Test 2c, Activated plugin in a private window should have visible 'Block' button." - ); - is( - gPrivateWindow.PopupNotifications.panel.firstElementChild.checkbox.hidden, - true, - "Test 2c, Activated plugin in a private window should not have visible 'Remember' checkbox." - ); - - clearAllPluginPermissions(); - - let promises = [ - BrowserTestUtils.browserLoaded( - gTestBrowser, - false, - gHttpTestRoot + "plugin_test.html" - ), - BrowserTestUtils.waitForEvent(window, "activate"), - ]; - gPrivateWindow.close(); - BrowserTestUtils.loadURI(gTestBrowser, gHttpTestRoot + "plugin_test.html"); - await Promise.all(promises); - await SimpleTest.promiseFocus(window); -}); - -add_task(async function test3a() { - // enable test plugin on this site - let popupNotification = PopupNotifications.getNotification( - "click-to-play-plugins", - gTestBrowser - ); - ok(popupNotification, "Test 3a, Should have a click-to-play notification"); - - await SpecialPowers.spawn(gTestBrowser, [], function() { - let plugin = content.document.getElementById("test"); - ok(!plugin.activated, "Test 3a, Plugin should not be activated"); - }); - - // Simulate clicking the "Allow" button. - let promiseShown = BrowserTestUtils.waitForEvent( - PopupNotifications.panel, - "Shown" - ); - popupNotification.reshow(); - await promiseShown; - PopupNotifications.panel.firstElementChild.button.click(); - - await SpecialPowers.spawn(gTestBrowser, [], async function() { - let plugin = content.document.getElementById("test"); - let condition = () => plugin.activated; - await ContentTaskUtils.waitForCondition( - condition, - "Test 3a, Waited too long for plugin to activate" - ); - }); -}); - -add_task(async function test3c() { - let topicObserved = TestUtils.topicObserved( - "PopupNotifications-updateNotShowing" - ); - await createPrivateWindow(gHttpTestRoot + "plugin_test.html"); - await topicObserved; - - let popupNotification = await TestUtils.waitForCondition(() => { - return gPrivateWindow.PopupNotifications.getNotification( - "click-to-play-plugins", - gPrivateBrowser - ); - }, "Waiting for click-to-play-plugins notification in the private window"); - ok(popupNotification, "Test 3c, Should have a click-to-play notification"); - - // Check the button status - let promiseShown = BrowserTestUtils.waitForEvent( - gPrivateWindow.PopupNotifications.panel, - "Shown" - ); - popupNotification.reshow(); - await promiseShown; - is( - gPrivateWindow.PopupNotifications.panel.firstElementChild.secondaryButton - .hidden, - false, - "Test 3c, Activated plugin in a private window should have visible 'Block' button." - ); - is( - gPrivateWindow.PopupNotifications.panel.firstElementChild.checkbox.hidden, - true, - "Test 3c, Activated plugin in a private window should not have visible 'Remember' checkbox." - ); - - BrowserTestUtils.loadURI( - gPrivateBrowser, - gHttpTestRoot + "plugin_two_types.html" - ); - await BrowserTestUtils.browserLoaded(gPrivateBrowser); - - finishTest(); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js firefox-85.0+build1/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js --- firefox-84.0.2+build1/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/browser_subframe_access_hidden_plugins.js 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -"use strict"; - -const TEST_PLUGIN_NAME = "Test Plug-in"; -const HIDDEN_CTP_PLUGIN_PREF = "plugins.navigator.hidden_ctp_plugin"; -const DOMAIN_1 = "http://example.com"; -const DOMAIN_2 = "http://mochi.test:8888"; - -/** - * If a plugin is click-to-play and named in HIDDEN_CTP_PLUGIN_PREF, - * then the plugin should be hidden in the navigator.plugins list by - * default. However, if a plugin has been allowed on a top-level - * document, we should let subframes of that document access - * navigator.plugins. - */ -add_task(async function setup() { - // We'll make the Test Plugin click-to-play. - let originalPluginState = getTestPluginEnabledState(); - setTestPluginEnabledState(Ci.nsIPluginTag.STATE_CLICKTOPLAY); - registerCleanupFunction(() => { - setTestPluginEnabledState(originalPluginState); - clearAllPluginPermissions(); - }); - - // And then make the plugin hidden. - await SpecialPowers.pushPrefEnv({ - set: [[HIDDEN_CTP_PLUGIN_PREF, TEST_PLUGIN_NAME]], - }); -}); - -add_task(async function test_plugin_accessible_in_subframe() { - // Let's make it so that DOMAIN_1 allows the test plugin to - // be activated. This permission will be cleaned up inside - // our registerCleanupFunction when the test ends. - let ssm = Services.scriptSecurityManager; - let principal = ssm.createContentPrincipalFromOrigin(DOMAIN_1); - let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService( - Ci.nsIPluginHost - ); - let permString = pluginHost.getPermissionStringForType("application/x-test"); - Services.perms.addFromPrincipal( - principal, - permString, - Ci.nsIPermissionManager.ALLOW_ACTION, - Ci.nsIPermissionManager.EXPIRE_NEVER, - 0 /* expireTime */ - ); - - await BrowserTestUtils.withNewTab( - { - gBrowser, - url: DOMAIN_1, - }, - async function(browser) { - await SpecialPowers.spawn( - browser, - [[TEST_PLUGIN_NAME, DOMAIN_2]], - async function([pluginName, domain2]) { - Assert.ok( - content.navigator.plugins[pluginName], - "Top-level document should find Test Plugin" - ); - - // Now manually create a subframe hosted at domain2... - let subframe = content.document.createElement("iframe"); - subframe.src = domain2; - let loadedPromise = ContentTaskUtils.waitForEvent(subframe, "load"); - content.document.body.appendChild(subframe); - await loadedPromise; - - // Make sure that the HiddenPlugin event never fires in content. - let sawEvent = false; - docShell.chromeEventHandler.addEventListener( - "HiddenPlugin", - function onHiddenPlugin(e) { - sawEvent = true; - docShell.chromeEventHandler.removeEventListener( - "HiddenPlugin", - onHiddenPlugin, - true - ); - }, - true - ); - - Assert.ok( - subframe.contentWindow.navigator.plugins[pluginName], - "Subframe should find Test Plugin" - ); - Assert.ok(!sawEvent, "Should not have seen the HiddenPlugin event."); - } - ); - } - ); -}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_add_dynamically.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_add_dynamically.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_add_dynamically.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_add_dynamically.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_alternate_content.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_alternate_content.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_alternate_content.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_alternate_content.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - - - - -

you should not see this link when plugins are click-to-play

-
- diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_big.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_big.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_big.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_big.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_both2.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_both2.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_both2.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_both2.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ - - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_both.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_both.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_both.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_both.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ - - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug744745.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug744745.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug744745.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug744745.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug749455.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug749455.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug749455.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug749455.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug787619.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug787619.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_bug787619.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_bug787619.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_crashCommentAndURL.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_crashCommentAndURL.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_crashCommentAndURL.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_crashCommentAndURL.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ - - - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_hidden_to_visible.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_hidden_to_visible.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_hidden_to_visible.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_hidden_to_visible.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ - - - - - - - - - diff -Nru firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_iframe.html firefox-85.0+build1/browser/base/content/test/plugins/plugin_iframe.html --- firefox-84.0.2+build1/browser/base/content/test/plugins/plugin_iframe.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/plugins/plugin_iframe.html 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ - - - - - - - + + diff -Nru firefox-84.0.2+build1/browser/base/content/test/protectionsUI/sandboxed.html^headers^ firefox-85.0+build1/browser/base/content/test/protectionsUI/sandboxed.html^headers^ --- firefox-84.0.2+build1/browser/base/content/test/protectionsUI/sandboxed.html^headers^ 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/protectionsUI/sandboxed.html^headers^ 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1 @@ +Content-Security-Policy: sandbox allow-scripts; diff -Nru firefox-84.0.2+build1/browser/base/content/test/sanitize/browser.ini firefox-85.0+build1/browser/base/content/test/sanitize/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/sanitize/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/sanitize/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -9,6 +9,7 @@ [browser_purgehistory_clears_sh.js] [browser_sanitize-formhistory.js] +[browser_sanitize-history.js] [browser_sanitize-offlineData.js] [browser_sanitize-passwordDisabledHosts.js] [browser_sanitize-sitepermissions.js] diff -Nru firefox-84.0.2+build1/browser/base/content/test/sanitize/browser_purgehistory_clears_sh.js firefox-85.0+build1/browser/base/content/test/sanitize/browser_purgehistory_clears_sh.js --- firefox-84.0.2+build1/browser/base/content/test/sanitize/browser_purgehistory_clears_sh.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/sanitize/browser_purgehistory_clears_sh.js 2021-01-18 19:58:08.000000000 +0000 @@ -33,7 +33,7 @@ content.history.pushState({}, ""); content.history.back(); await new Promise(function(r) { - content.setTimeout(r); + content.onpopstate = r; }); let newHistory = content.history.length; Assert.equal(startHistory, 1, "Initial SHistory size"); diff -Nru firefox-84.0.2+build1/browser/base/content/test/sanitize/browser_sanitize-history.js firefox-85.0+build1/browser/base/content/test/sanitize/browser_sanitize-history.js --- firefox-84.0.2+build1/browser/base/content/test/sanitize/browser_sanitize-history.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/sanitize/browser_sanitize-history.js 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1,129 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests that sanitizing history will clear storage access permissions +// for sites without cookies or site data. +add_task(async function sanitizeStorageAccessPermissions() { + await new Promise(resolve => { + Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve); + }); + + await SiteDataTestUtils.addToIndexedDB("https://sub.example.org"); + await SiteDataTestUtils.addToCookies("https://example.com"); + + PermissionTestUtils.add( + "https://example.org", + "storageAccessAPI", + Services.perms.ALLOW_ACTION + ); + PermissionTestUtils.add( + "https://example.com", + "storageAccessAPI", + Services.perms.ALLOW_ACTION + ); + PermissionTestUtils.add( + "http://mochi.test", + "storageAccessAPI", + Services.perms.ALLOW_ACTION + ); + + // Add some time in between taking the snapshot of the timestamp + // to avoid flakyness. + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(c => setTimeout(c, 100)); + let timestamp = Date.now(); + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + await new Promise(c => setTimeout(c, 100)); + + PermissionTestUtils.add( + "http://example.net", + "storageAccessAPI", + Services.perms.ALLOW_ACTION + ); + + await Sanitizer.sanitize(["history"], { range: [timestamp, Date.now()] }); + + Assert.equal( + PermissionTestUtils.testExactPermission( + "http://example.net", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "http://mochi.test", + "storageAccessAPI" + ), + Services.perms.ALLOW_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.com", + "storageAccessAPI" + ), + Services.perms.ALLOW_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.org", + "storageAccessAPI" + ), + Services.perms.ALLOW_ACTION + ); + + await Sanitizer.sanitize(["history"]); + + Assert.equal( + PermissionTestUtils.testExactPermission( + "http://mochi.test", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "http://example.net", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.com", + "storageAccessAPI" + ), + Services.perms.ALLOW_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.org", + "storageAccessAPI" + ), + Services.perms.ALLOW_ACTION + ); + + await Sanitizer.sanitize(["history", "siteSettings"]); + + Assert.equal( + PermissionTestUtils.testExactPermission( + "http://mochi.test", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.com", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); + Assert.equal( + PermissionTestUtils.testExactPermission( + "https://example.org", + "storageAccessAPI" + ), + Services.perms.UNKNOWN_ACTION + ); +}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_check_identity_state.js 2021-01-18 19:58:08.000000000 +0000 @@ -544,7 +544,7 @@ is( getSecurityConnectionBG(), - `url("chrome://browser/skin/connection-mixed-passive-loaded.svg")`, + `url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")`, "Security connection should show a warning lock icon." ); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_deprecatedTLSVersions.js 2021-01-18 19:58:08.000000000 +0000 @@ -46,7 +46,7 @@ await BrowserTestUtils.withNewTab("about:blank", async function(browser) { // Try deprecated versions - await BrowserTestUtils.loadURI(browser, HTTPS_TLS1_0); + BrowserTestUtils.loadURI(browser, HTTPS_TLS1_0); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "broken"); is( @@ -56,7 +56,7 @@ ); await checkConnectionState("not-secure"); - await BrowserTestUtils.loadURI(browser, HTTPS_TLS1_1); + BrowserTestUtils.loadURI(browser, HTTPS_TLS1_1); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "broken"); is( @@ -67,14 +67,14 @@ await checkConnectionState("not-secure"); // Transition to secure - await BrowserTestUtils.loadURI(browser, HTTPS_TLS1_2); + BrowserTestUtils.loadURI(browser, HTTPS_TLS1_2); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "secure"); is(getIdentityMode(), "verifiedDomain", "Identity should be verified"); await checkConnectionState("secure"); // Transition back to broken - await BrowserTestUtils.loadURI(browser, HTTPS_TLS1_1); + BrowserTestUtils.loadURI(browser, HTTPS_TLS1_1); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "broken"); is( @@ -85,7 +85,7 @@ await checkConnectionState("not-secure"); // TLS1.3 for completeness - await BrowserTestUtils.loadURI(browser, HTTPS_TLS1_3); + BrowserTestUtils.loadURI(browser, HTTPS_TLS1_3); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "secure"); is(getIdentityMode(), "verifiedDomain", "Identity should be verified"); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_geolocation_indicator.js 2021-01-18 19:58:08.000000000 +0000 @@ -3,6 +3,8 @@ "use strict"; +requestLongerTimeout(2); + ChromeUtils.import("resource:///modules/PermissionUI.jsm", this); ChromeUtils.import("resource:///modules/SitePermissions.jsm", this); const { PermissionTestUtils } = ChromeUtils.import( @@ -43,10 +45,11 @@ async function checkForDOMElement(state, id) { info(`Testing state ${state} of element ${id}`); + let el; try { await BrowserTestUtils.waitForCondition( () => { - let el = document.getElementById(id); + el = document.getElementById(id); return el != null; }, `Waiting for ${id}`, @@ -54,15 +57,23 @@ ); } catch (e) { ok(!state, `${id} has correct state`); - return; + return el; } ok(state, `${id} has correct state`); + + return el; } async function testIdentityPopupGeoContainer( containerVisible, timestampVisible ) { + // The container holds the timestamp element, therefore we can't have a + // visible timestamp without the container. + if (timestampVisible && !containerVisible) { + ok(false, "Can't have timestamp without container"); + } + // Only call openIdentityPopup if popup is closed, otherwise it does not resolve if (!gIdentityHandler._identityBox.hasAttribute("open")) { await openIdentityPopup(); @@ -72,6 +83,26 @@ containerVisible, "identity-popup-geo-container" ); + + if (containerVisible && timestampVisible) { + // Wait for the geo container to be fully populated. + // The time label is computed async. + let container = await checkContainer; + await BrowserTestUtils.waitForCondition( + () => container.childElementCount == 2, + "identity-popup-geo-container should have two elements." + ); + is( + container.childNodes[0].classList[0], + "identity-popup-permission-item", + "Geo container should have permission item." + ); + is( + container.childNodes[1].id, + "geo-access-indicator-item", + "Geo container should have indicator item." + ); + } let checkAccessIndicator = checkForDOMElement( timestampVisible, "geo-access-indicator-item" @@ -334,3 +365,17 @@ ]); await cleanup(tab); }); + +/** + * Tests that we only show the last access label once when the sharing + * state is updated multiple times while the popup is open. + */ +add_task(async function test_identity_no_duplicate_last_access_label() { + let tab = await openExamplePage(); + await setGeoLastAccess(tab.linkedBrowser, true); + await openIdentityPopup(); + gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true }); + gBrowser.updateBrowserSharing(tab.linkedBrowser, { geo: true }); + await testIdentityPopupGeoContainer(true, true); + await cleanup(tab); +}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_getSecurityInfo.js 2021-01-18 19:58:08.000000000 +0000 @@ -14,7 +14,7 @@ add_task(async function test() { await BrowserTestUtils.withNewTab("about:blank", async function(browser) { let loaded = BrowserTestUtils.waitForErrorPage(browser); - await BrowserTestUtils.loadURI(browser, "https://self-signed.example.com"); + BrowserTestUtils.loadURI(browser, "https://self-signed.example.com"); await loaded; let securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo(); @@ -33,14 +33,14 @@ ); loaded = BrowserTestUtils.browserLoaded(browser); - await BrowserTestUtils.loadURI(browser, "http://example.com"); + BrowserTestUtils.loadURI(browser, "http://example.com"); await loaded; securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo(); ok(!securityInfo, "Found no security info"); loaded = BrowserTestUtils.browserLoaded(browser); - await BrowserTestUtils.loadURI(browser, "https://example.com"); + BrowserTestUtils.loadURI(browser, "https://example.com"); await loaded; securityInfo = await browser.browsingContext.currentWindowGlobal.getSecurityInfo(); @@ -55,7 +55,7 @@ ); loaded = BrowserTestUtils.browserLoaded(browser); - await BrowserTestUtils.loadURI(browser, IFRAME_PAGE); + BrowserTestUtils.loadURI(browser, IFRAME_PAGE); await loaded; // Get the info of the parent, which is HTTP. diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_identityIcon_img_url.js 2021-01-18 19:58:08.000000000 +0000 @@ -20,7 +20,7 @@ { type: "http", testURL: "http://example.com", - img_url: `url("chrome://browser/skin/connection-mixed-active-loaded.svg")`, + img_url: `url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")`, }, { type: "https", @@ -50,7 +50,7 @@ { type: "mixedPassiveContent", testURL: kBaseURI + "file_mixedPassiveContent.html", - img_url: `url("chrome://browser/skin/connection-mixed-passive-loaded.svg")`, + img_url: `url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")`, }, { type: "mixedActiveContent", @@ -60,7 +60,7 @@ { type: "certificateError", testURL: "https://self-signed.example.com", - img_url: `url("chrome://browser/skin/connection-mixed-passive-loaded.svg")`, + img_url: `url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")`, }, { type: "localhost", @@ -75,12 +75,12 @@ { type: "data URI", testURL: "data:text/html,
", - img_url: `url("chrome://browser/skin/connection-mixed-active-loaded.svg")`, + img_url: `url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")`, }, { type: "view-source HTTP", testURL: "view-source:http://example.com/", - img_url: `url("chrome://browser/skin/connection-mixed-active-loaded.svg")`, + img_url: `url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")`, }, { type: "view-source HTTPS", diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser.ini firefox-85.0+build1/browser/base/content/test/siteIdentity/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -122,3 +122,4 @@ support-files = file_mixedPassiveContent.html file_bug1045809_1.html +[browser_tab_sharing_state.js] diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js 2021-01-18 19:58:08.000000000 +0000 @@ -25,7 +25,7 @@ function checkIdentityPopup(icon) { gIdentityHandler.refreshIdentityPopup(); - is(getIdentityIcon(), `url("chrome://browser/skin/${icon}")`); + is(getIdentityIcon(), `url("chrome://global/skin/icons/${icon}")`); is(getConnectionState(), "secure-cert-user-overridden"); isnot( getPopupContentVerifier().style.display, @@ -52,7 +52,7 @@ checkIdentityPopup("connection-mixed-active-loaded.svg"); // check that a warning is shown even without mixed content - await BrowserTestUtils.loadURI( + BrowserTestUtils.loadURI( gBrowser.selectedBrowser, "https://self-signed.example.com" ); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixedContentFromOnunload.js 2021-01-18 19:58:08.000000000 +0000 @@ -38,7 +38,7 @@ // Navigation from an http page to a https page with no mixed content // The http page loads an http image on unload url = HTTPS_TEST_ROOT_1 + "file_mixedContentFromOnunload_test1.html"; - await BrowserTestUtils.loadURI(browser, url); + BrowserTestUtils.loadURI(browser, url); await BrowserTestUtils.browserLoaded(browser); // check security state. Since current url is https and doesn't have any // mixed content resources, we expect it to be secure. @@ -51,10 +51,10 @@ // Navigation from an http page to a https page that has mixed display content // The https page loads an http image on unload url = HTTP_TEST_ROOT_2 + "file_mixedContentFromOnunload.html"; - await BrowserTestUtils.loadURI(browser, url); + BrowserTestUtils.loadURI(browser, url); await BrowserTestUtils.browserLoaded(browser); url = HTTPS_TEST_ROOT_2 + "file_mixedContentFromOnunload_test2.html"; - await BrowserTestUtils.loadURI(browser, url); + BrowserTestUtils.loadURI(browser, url); await BrowserTestUtils.browserLoaded(browser); isSecurityState(browser, "broken"); await assertMixedContentBlockingState(browser, { diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_mixed_content_with_navigation.js 2021-01-18 19:58:08.000000000 +0000 @@ -49,7 +49,7 @@ // seem to work with withNewTab. await BrowserTestUtils.withNewTab("about:blank", async browser => { // Navigate to the test URI. - await BrowserTestUtils.loadURI(browser, testcase.uri); + BrowserTestUtils.loadURI(browser, testcase.uri); if (!testcase.expectErrorPage) { await BrowserTestUtils.browserLoaded(browser, false, testcase.uri); } else { @@ -62,7 +62,7 @@ ); // Navigate to a URI that should be secure. - await BrowserTestUtils.loadURI(browser, kSecureURI); + BrowserTestUtils.loadURI(browser, kSecureURI); await BrowserTestUtils.browserLoaded(browser, false, kSecureURI); let secureIdentityMode = window.document.getElementById("identity-box") .className; @@ -91,7 +91,7 @@ is(secureIdentityMode, "verifiedDomain", "identity should start as secure"); // Navigate to the test URI. - await BrowserTestUtils.loadURI(browser, testcase.uri); + BrowserTestUtils.loadURI(browser, testcase.uri); if (!testcase.expectErrorPage) { await BrowserTestUtils.browserLoaded(browser, false, testcase.uri); } else { diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js 1970-01-01 00:00:00.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/browser_tab_sharing_state.js 2021-01-18 19:58:08.000000000 +0000 @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Tests gBrowser#updateBrowserSharing + */ +add_task(async function testBrowserSharingStateSetter() { + const WEBRTC_TEST_STATE = { + camera: 0, + microphone: 1, + paused: false, + sharing: "microphone", + showMicrophoneIndicator: true, + showScreenSharingIndicator: "", + windowId: 0, + }; + + const WEBRTC_TEST_STATE2 = { + camera: 1, + microphone: 1, + paused: false, + sharing: "camera", + showCameraIndicator: true, + showMicrophoneIndicator: true, + showScreenSharingIndicator: "", + windowId: 1, + }; + + await BrowserTestUtils.withNewTab("https://example.com", async browser => { + let tab = gBrowser.selectedTab; + is(tab._sharingState, undefined, "No sharing state initially."); + ok(!tab.hasAttribute("sharing"), "No tab sharing attribute initially."); + + // Set an active sharing state for webrtc + gBrowser.updateBrowserSharing(browser, { webRTC: WEBRTC_TEST_STATE }); + Assert.deepEqual( + tab._sharingState, + { webRTC: WEBRTC_TEST_STATE }, + "Should have correct webRTC sharing state." + ); + is( + tab.getAttribute("sharing"), + WEBRTC_TEST_STATE.sharing, + "Tab sharing attribute reflects webRTC sharing state." + ); + + // Set sharing state for geolocation + gBrowser.updateBrowserSharing(browser, { geo: true }); + Assert.deepEqual( + tab._sharingState, + { + webRTC: WEBRTC_TEST_STATE, + geo: true, + }, + "Should have sharing state for both webRTC and geolocation." + ); + is( + tab.getAttribute("sharing"), + WEBRTC_TEST_STATE.sharing, + "Geolocation sharing doesn't update the tab sharing attribute." + ); + + // Update webRTC sharing state + gBrowser.updateBrowserSharing(browser, { webRTC: WEBRTC_TEST_STATE2 }); + Assert.deepEqual( + tab._sharingState, + { geo: true, webRTC: WEBRTC_TEST_STATE2 }, + "Should have updated webRTC sharing state while maintaining geolocation state." + ); + is( + tab.getAttribute("sharing"), + WEBRTC_TEST_STATE2.sharing, + "Tab sharing attribute reflects webRTC sharing state." + ); + + // Clear webRTC sharing state + gBrowser.updateBrowserSharing(browser, { webRTC: null }); + Assert.deepEqual( + tab._sharingState, + { geo: true, webRTC: null }, + "Should only have sharing state for geolocation." + ); + ok( + !tab.hasAttribute("sharing"), + "Ending webRTC sharing should remove tab sharing attribute." + ); + + // Clear geolocation sharing state + gBrowser.updateBrowserSharing(browser, { geo: null }); + Assert.deepEqual(tab._sharingState, { geo: null, webRTC: null }); + ok( + !tab.hasAttribute("sharing"), + "Tab sharing attribute should not be set." + ); + }); +}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/siteIdentity/head.js firefox-85.0+build1/browser/base/content/test/siteIdentity/head.js --- firefox-84.0.2+build1/browser/base/content/test/siteIdentity/head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/siteIdentity/head.js 2021-01-18 19:58:08.000000000 +0000 @@ -213,7 +213,7 @@ if (activeLoaded) { is( identityIconImage, - 'url("chrome://browser/skin/connection-mixed-active-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")', "Using active loaded icon" ); } @@ -227,14 +227,14 @@ if (passiveLoaded && !(activeLoaded || activeBlocked)) { is( identityIconImage, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "Using passive loaded icon" ); } if (passiveLoaded && activeBlocked) { is( identityIconImage, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "Using active blocked and passive loaded icon" ); } @@ -309,12 +309,12 @@ if (stateInsecure) { is( securityViewBG, - 'url("chrome://browser/skin/connection-mixed-active-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")', "CC using 'not secure' icon" ); is( securityContentBG, - 'url("chrome://browser/skin/connection-mixed-active-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-active-loaded.svg")', "CC using 'not secure' icon" ); } @@ -347,12 +347,12 @@ } else if (activeBlocked || passiveLoaded) { is( securityViewBG, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "CC using degraded icon" ); is( securityContentBG, - 'url("chrome://browser/skin/connection-mixed-passive-loaded.svg")', + 'url("chrome://global/skin/icons/connection-mixed-passive-loaded.svg")', "CC using degraded icon" ); } else { @@ -402,7 +402,7 @@ async function loadBadCertPage(url) { let loaded = BrowserTestUtils.waitForErrorPage(gBrowser.selectedBrowser); - await BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url); + BrowserTestUtils.loadURI(gBrowser.selectedBrowser, url); await loaded; await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() { diff -Nru firefox-84.0.2+build1/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js firefox-85.0+build1/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js --- firefox-84.0.2+build1/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/startup/browser_preXULSkeletonUIRegistry.js 2021-01-18 19:58:08.000000000 +0000 @@ -70,10 +70,19 @@ ); is(enabled, 0, "Pre-XUL skeleton UI is disabled in the Windows registry"); + Services.prefs.setBoolPref("browser.startup.preXulSkeletonUI", true); + Services.prefs.setBoolPref("browser.tabs.drawInTitlebar", false); + enabled = WindowsRegistry.readRegKey( + Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, + "Software\\Mozilla\\Firefox\\PreXULSkeletonUISettings", + `${firefoxPath}|Enabled` + ); + is(enabled, 0, "Pre-XUL skeleton UI is disabled in the Windows registry"); + await BrowserTestUtils.closeWindow(win); }); -add_task(async function testWritesSizeValuesOnChange() { +add_task(async function testPersistsNecessaryValuesOnChange() { // Enable the skeleton UI, since if it's disabled we won't persist the size values await SpecialPowers.pushPrefEnv({ set: [["browser.startup.preXulSkeletonUI", true]], @@ -89,6 +98,7 @@ "SpringsCSSSpan", "SearchbarCSSSpan", "Theme", + "MenubarShown", ]; // Remove all of the registry values to ensure old tests aren't giving us false @@ -109,9 +119,10 @@ "Software\\Mozilla\\Firefox\\PreXULSkeletonUISettings", `${firefoxPath}|${key}` ); - ok( - value, - `Skeleton UI registry values should have a non-zero value for ${key}` + isnot( + typeof value, + "undefined", + `Skeleton UI registry values should have a defined value for ${key}` ); if (value.length) { let hasNonZero = false; diff -Nru firefox-84.0.2+build1/browser/base/content/test/static/browser.ini firefox-85.0+build1/browser/base/content/test/static/browser.ini --- firefox-84.0.2+build1/browser/base/content/test/static/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/static/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -1,8 +1,8 @@ [DEFAULT] # These tests can be prone to intermittent failures on slower systems. # Since the specific flavor doesn't matter from a correctness standpoint, -# just skip the tests on ASAN and debug builds. -skip-if = asan || debug +# just skip the tests on sanitizer and debug builds. +skip-if = asan || tsan || debug support-files = head.js diff -Nru firefox-84.0.2+build1/browser/base/content/test/tabdialogs/browser_subdialog_esc.js firefox-85.0+build1/browser/base/content/test/tabdialogs/browser_subdialog_esc.js --- firefox-84.0.2+build1/browser/base/content/test/tabdialogs/browser_subdialog_esc.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/tabdialogs/browser_subdialog_esc.js 2021-01-18 19:58:08.000000000 +0000 @@ -54,3 +54,65 @@ ok(true, "Load completed"); }); }); + +/** + * Tests that ESC on a SubDialog with an open dropdown doesn't close the dialog. + */ +add_task(async function test_subdialog_esc_on_dropdown_does_not_close_dialog() { + await BrowserTestUtils.withNewTab("http://example.com", async function( + browser + ) { + // Open the test dialog + let dialogBox = gBrowser.getTabDialogBox(browser); + let dialogClose = dialogBox.open(TEST_DIALOG_PATH, { + keepOpenSameOriginNav: true, + }); + + let dialogs = dialogBox._dialogManager._dialogs; + + is(dialogs.length, 1, "Dialog manager has a dialog."); + + let dialog = dialogs[0]; + + info("Waiting for dialog to open."); + await dialog._dialogReady; + + // Open dropdown + let select = dialog._frame.contentDocument.getElementById("select"); + let shownPromise = BrowserTestUtils.waitForEvent( + select, + "popupshowing", + true + ); + + info("Opening dropdown"); + select.focus(); + EventUtils.synthesizeKey("VK_SPACE", {}, dialog._frame.contentWindow); + + await shownPromise; + + let hiddenPromise = BrowserTestUtils.waitForEvent( + select, + "popuphiding", + true + ); + + // Race dropdown closing vs SubDialog close + let race = Promise.race([ + hiddenPromise.then(() => true), + dialogClose.then(() => false), + ]); + + // Close the dropdown with esc key + info("Hitting escape key."); + await EventUtils.synthesizeKey("KEY_Escape"); + + let result = await race; + ok(result, "Select closed first"); + + await new Promise(resolve => executeSoon(resolve)); + + ok(!dialog._isClosing, "Dialog is not closing"); + ok(dialog._openedURL, "Dialog is open"); + }); +}); diff -Nru firefox-84.0.2+build1/browser/base/content/test/tabdialogs/subdialog.xhtml firefox-85.0+build1/browser/base/content/test/tabdialogs/subdialog.xhtml --- firefox-84.0.2+build1/browser/base/content/test/tabdialogs/subdialog.xhtml 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/tabdialogs/subdialog.xhtml 2021-01-18 19:58:08.000000000 +0000 @@ -7,8 +7,7 @@ + title="Sample sub-dialog"> + + diff -Nru firefox-84.0.2+build1/browser/base/content/test/zoom/head.js firefox-85.0+build1/browser/base/content/test/zoom/head.js --- firefox-84.0.2+build1/browser/base/content/test/zoom/head.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/test/zoom/head.js 2021-01-18 19:58:08.000000000 +0000 @@ -219,7 +219,7 @@ let loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, handle); if (url) { - await BrowserTestUtils.loadURI(tab.linkedBrowser, url); + BrowserTestUtils.loadURI(tab.linkedBrowser, url); } return loaded; diff -Nru firefox-84.0.2+build1/browser/base/content/webext-panels.js firefox-85.0+build1/browser/base/content/webext-panels.js --- firefox-84.0.2+build1/browser/base/content/webext-panels.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/base/content/webext-panels.js 2021-01-18 19:58:08.000000000 +0000 @@ -38,6 +38,7 @@ browser.setAttribute("type", "content"); browser.setAttribute("flex", "1"); browser.setAttribute("disableglobalhistory", "true"); + browser.setAttribute("messagemanagergroup", "webext-browsers"); browser.setAttribute("webextension-view-type", panel.viewType); browser.setAttribute("context", "contentAreaContextMenu"); browser.setAttribute("tooltip", "aHTMLTooltip"); diff -Nru firefox-84.0.2+build1/browser/branding/aurora/pref/firefox-branding.js firefox-85.0+build1/browser/branding/aurora/pref/firefox-branding.js --- firefox-84.0.2+build1/browser/branding/aurora/pref/firefox-branding.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/branding/aurora/pref/firefox-branding.js 2021-01-18 19:58:08.000000000 +0000 @@ -18,6 +18,7 @@ pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/aurora/"); pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); +pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog"); // The number of days a binary is permitted to be old // without checking for an update. This assumes that diff -Nru firefox-84.0.2+build1/browser/branding/nightly/pref/firefox-branding.js firefox-85.0+build1/browser/branding/nightly/pref/firefox-branding.js --- firefox-84.0.2+build1/browser/branding/nightly/pref/firefox-branding.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/branding/nightly/pref/firefox-branding.js 2021-01-18 19:58:08.000000000 +0000 @@ -19,6 +19,7 @@ pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/nightly/notes/"); pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); +pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog"); // The number of days a binary is permitted to be old // without checking for an update. This assumes that diff -Nru firefox-84.0.2+build1/browser/branding/official/pref/firefox-branding.js firefox-85.0+build1/browser/branding/official/pref/firefox-branding.js --- firefox-84.0.2+build1/browser/branding/official/pref/firefox-branding.js 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/branding/official/pref/firefox-branding.js 2021-01-18 19:58:08.000000000 +0000 @@ -20,10 +20,12 @@ pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/beta"); pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/beta/notes"); pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); + pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%beta/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog"); #else pref("app.update.url.manual", "https://www.mozilla.org/%LOCALE%/firefox/"); pref("app.update.url.details", "https://www.mozilla.org/%LOCALE%/firefox/notes"); pref("app.releaseNotesURL", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=whatsnew"); + pref("app.releaseNotesURL.aboutDialog", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/releasenotes/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog"); #endif // The number of days a binary is permitted to be old diff -Nru firefox-84.0.2+build1/browser/components/aboutconfig/test/browser/browser.ini firefox-85.0+build1/browser/components/aboutconfig/test/browser/browser.ini --- firefox-84.0.2+build1/browser/components/aboutconfig/test/browser/browser.ini 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/components/aboutconfig/test/browser/browser.ini 2021-01-18 19:58:08.000000000 +0000 @@ -1,5 +1,5 @@ [DEFAULT] -skip-if = debug || asan # Bug 1507747 and bug 1520398 +skip-if = debug || asan || tsan # Bug 1507747 and bug 1520398 support-files = head.js diff -Nru firefox-84.0.2+build1/browser/components/aboutlogins/AboutLoginsChild.jsm firefox-85.0+build1/browser/components/aboutlogins/AboutLoginsChild.jsm --- firefox-84.0.2+build1/browser/components/aboutlogins/AboutLoginsChild.jsm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/components/aboutlogins/AboutLoginsChild.jsm 2021-01-18 19:58:08.000000000 +0000 @@ -180,6 +180,10 @@ recordTelemetryEvent(event.detail); break; } + case "AboutLoginsRemoveAllLogins": { + this.sendAsyncMessage("AboutLogins:RemoveAllLogins"); + break; + } case "AboutLoginsSortChanged": { this.sendAsyncMessage("AboutLogins:SortChanged", event.detail); break; diff -Nru firefox-84.0.2+build1/browser/components/aboutlogins/AboutLoginsParent.jsm firefox-85.0+build1/browser/components/aboutlogins/AboutLoginsParent.jsm --- firefox-84.0.2+build1/browser/components/aboutlogins/AboutLoginsParent.jsm 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/components/aboutlogins/AboutLoginsParent.jsm 2021-01-18 19:58:08.000000000 +0000 @@ -426,6 +426,10 @@ fp.open(fpCallback); break; } + case "AboutLogins:RemoveAllLogins": { + Services.logins.removeAllUserFacingLogins(); + break; + } } } @@ -790,12 +794,14 @@ // authenticated. More diagnostics and error states can be handled // by other more Sync-specific pages. const loggedIn = state.status != UIState.STATUS_NOT_CONFIGURED; + const passwordSyncEnabled = state.syncEnabled && PASSWORD_SYNC_ENABLED; return { loggedIn, email: state.email, avatarURL: state.avatarURL, fxAccountsEnabled: FXA_ENABLED, + passwordSyncEnabled, }; }, @@ -815,6 +821,7 @@ onPasswordSyncEnabledPreferenceChange(data, previous, latest) { Services.prefs.clearUserPref(SHOW_PASSWORD_SYNC_NOTIFICATION_PREF); this.updatePasswordSyncNotificationState(this.getSyncState(), latest); + this.messageSubscribers("AboutLogins:SyncState", this.getSyncState()); }, }; var _AboutLogins = AboutLogins; diff -Nru firefox-84.0.2+build1/browser/components/aboutlogins/content/aboutLogins.css firefox-85.0+build1/browser/components/aboutlogins/content/aboutLogins.css --- firefox-84.0.2+build1/browser/components/aboutlogins/content/aboutLogins.css 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/components/aboutlogins/content/aboutLogins.css 2021-01-18 19:58:08.000000000 +0000 @@ -56,7 +56,7 @@ :root:not(.initialized) login-intro, :root:not(.initialized) login-item, :root.empty-search login-intro, -:root:not(.no-logins):not(.empty-search):not(.login-selected) login-intro, +:root:not(.no-logins, .empty-search, .login-selected) login-intro, login-item[data-editing="true"] + login-intro, .login-selected login-intro, :root:not(.login-selected) login-item:not([data-editing="true"]), diff -Nru firefox-84.0.2+build1/browser/components/aboutlogins/content/aboutLogins.html firefox-85.0+build1/browser/components/aboutlogins/content/aboutLogins.html --- firefox-84.0.2+build1/browser/components/aboutlogins/content/aboutLogins.html 2021-01-06 11:23:05.000000000 +0000 +++ firefox-85.0+build1/browser/components/aboutlogins/content/aboutLogins.html 2021-01-18 19:58:08.000000000 +0000 @@ -13,6 +13,7 @@ + @@ -37,6 +38,7 @@ +
+ +