diff --git a/accessible/public/nsIAccessibleRole.idl b/accessible/public/nsIAccessibleRole.idl index 49b86dbb4c12..567d3bb246c2 100644 --- a/accessible/public/nsIAccessibleRole.idl +++ b/accessible/public/nsIAccessibleRole.idl @@ -781,10 +781,16 @@ interface nsIAccessibleRole : nsISupports */ const unsigned long ROLE_EMBEDDED_OBJECT = 122; + /** + * A note. Originally intended to be hidden until activated, but now also used + * for things like html 'aside'. + */ + const unsigned long ROLE_NOTE = 123; + /** * It's not role actually. This constant is important to help ensure * nsRoleMap's are synchronized. */ - const unsigned long ROLE_LAST_ENTRY = 123; + const unsigned long ROLE_LAST_ENTRY = 124; }; diff --git a/accessible/src/atk/nsRoleMap.h b/accessible/src/atk/nsRoleMap.h index e41a84e86cf3..58fabd078249 100644 --- a/accessible/src/atk/nsRoleMap.h +++ b/accessible/src/atk/nsRoleMap.h @@ -169,6 +169,7 @@ static const PRUint32 atkRoleMap[] = { ATK_ROLE_UNKNOWN, // nsIAccessibleRole::ROLE_FLAT_EQUATION 120 ATK_ROLE_TABLE_CELL, // nsIAccessibleRole::ROLE_GRID_CELL 121 ATK_ROLE_PANEL, // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT 122 + ATK_ROLE_SECTION, // nsIAccessibleRole::ROLE_NOTE 123 kROLE_ATK_LAST_ENTRY // nsIAccessibleRole::ROLE_LAST_ENTRY }; diff --git a/accessible/src/base/nsAccDocManager.cpp b/accessible/src/base/nsAccDocManager.cpp index f64fd16136b5..f36473f10f7c 100644 --- a/accessible/src/base/nsAccDocManager.cpp +++ b/accessible/src/base/nsAccDocManager.cpp @@ -456,28 +456,29 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument) // We only create root accessibles for the true root, otherwise create a // doc accessible. nsCOMPtr weakShell(do_GetWeakReference(presShell)); - nsDocAccessible *docAcc = isRootDoc ? + nsRefPtr docAcc = isRootDoc ? new nsRootAccessibleWrap(aDocument, rootElm, weakShell) : new nsDocAccessibleWrap(aDocument, rootElm, weakShell); - if (!docAcc) + // Cache the document accessible into document cache. + if (!docAcc || !mDocAccessibleCache.Put(aDocument, docAcc)) return nsnull; - // Cache and addref document accessible. - if (!mDocAccessibleCache.Put(aDocument, docAcc)) { - delete docAcc; - return nsnull; - } - - // XXX: ideally we should initialize an accessible and then put it into tree, - // we can't since document accessible fires reorder event on its container - // while initialized. - if (!outerDocAcc->AppendChild(docAcc) || - !GetAccService()->InitAccessible(docAcc, nsAccUtils::GetRoleMapEntry(aDocument))) { + // Bind the document accessible into tree. + if (!outerDocAcc->AppendChild(docAcc)) { mDocAccessibleCache.Remove(aDocument); return nsnull; } + // Initialize the document accessible. Note, Init() should be called after + // the document accessible is bound to the tree. + if (!docAcc->Init()) { + docAcc->Shutdown(); + mDocAccessibleCache.Remove(aDocument); + return nsnull; + } + docAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aDocument)); + NS_LOG_ACCDOCCREATE("document creation finished", aDocument) AddListeners(aDocument, isRootDoc); diff --git a/accessible/src/base/nsAccTreeWalker.cpp b/accessible/src/base/nsAccTreeWalker.cpp index 60fc4d284bee..fc04ba0a1ce5 100644 --- a/accessible/src/base/nsAccTreeWalker.cpp +++ b/accessible/src/base/nsAccTreeWalker.cpp @@ -113,16 +113,16 @@ nsAccTreeWalker::GetNextChildInternal(PRBool aNoWalkUp) nsIContent* childNode = mState->childList->GetNodeAt(mState->childIdx); mState->childIdx++; - PRBool isHidden = PR_FALSE; + bool isSubtreeHidden = false; nsRefPtr accessible = GetAccService()->GetOrCreateAccessible(childNode, presShell, mWeakShell, - &isHidden); + &isSubtreeHidden); if (accessible) return accessible.forget(); // Walk down into subtree to find accessibles. - if (!isHidden) { + if (!isSubtreeHidden) { if (!PushState(childNode)) break; diff --git a/accessible/src/base/nsAccessibilityAtomList.h b/accessible/src/base/nsAccessibilityAtomList.h index 062542fb12ed..d576d6281650 100644 --- a/accessible/src/base/nsAccessibilityAtomList.h +++ b/accessible/src/base/nsAccessibilityAtomList.h @@ -101,6 +101,7 @@ ACCESSIBILITY_ATOM(abbr, "abbr") ACCESSIBILITY_ATOM(acronym, "acronym") ACCESSIBILITY_ATOM(area, "area") ACCESSIBILITY_ATOM(article, "article") // HTML landmark +ACCESSIBILITY_ATOM(aside, "aside") // HTML landmark ACCESSIBILITY_ATOM(autocomplete, "autocomplete") ACCESSIBILITY_ATOM(blockquote, "blockquote") ACCESSIBILITY_ATOM(br, "br") diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index 30c537e727aa..1cc59b595dbd 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -837,28 +837,6 @@ nsAccessibilityService::GetCachedAccessibleOrContainer(nsINode* aNode) return accessible; } -PRBool -nsAccessibilityService::InitAccessible(nsAccessible *aAccessible, - nsRoleMapEntry *aRoleMapEntry) -{ - if (!aAccessible) - return PR_FALSE; - - // Add to cache an accessible, etc. - if (!aAccessible->Init()) { - NS_ERROR("Failed to initialize an accessible!"); - - aAccessible->Shutdown(); - return PR_FALSE; - } - - NS_ASSERTION(aAccessible->IsInCache(), - "Initialized accessible not in the cache!"); - - aAccessible->SetRoleMapEntry(aRoleMapEntry); - return PR_TRUE; -} - static PRBool HasRelatedContent(nsIContent *aContent) { nsAutoString id; @@ -891,13 +869,13 @@ already_AddRefed nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, nsIPresShell* aPresShell, nsIWeakReference* aWeakShell, - PRBool* aIsHidden) + bool* aIsSubtreeHidden) { if (!aPresShell || !aWeakShell || !aNode || gIsShutdown) return nsnull; - if (aIsHidden) - *aIsHidden = PR_FALSE; + if (aIsSubtreeHidden) + *aIsSubtreeHidden = false; // Check to see if we already have an accessible for this node in the cache. nsAccessible *cachedAccessible = GetCachedAccessible(aNode, aWeakShell); @@ -938,10 +916,11 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, // methods on a dead frame pointer. nsWeakFrame weakFrame = content->GetPrimaryFrame(); - // Check frame to see if it is hidden. - if (!weakFrame.GetFrame()) { - if (aIsHidden) - *aIsHidden = PR_TRUE; + // Check frame and its visibility. Note, hidden frame allows visible + // elements in subtree. + if (!weakFrame.GetFrame() || !weakFrame->GetStyleVisibility()->IsVisible()) { + if (aIsSubtreeHidden && !weakFrame.GetFrame()) + *aIsSubtreeHidden = true; return nsnull; } @@ -958,6 +937,13 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, return areaAcc; } + nsDocAccessible* docAcc = + GetAccService()->GetDocAccessible(aNode->GetOwnerDoc()); + if (!docAcc) { + NS_NOTREACHED("No document for accessible being created!"); + return nsnull; + } + // Attempt to create an accessible based on what we know. nsRefPtr newAcc; if (content->IsNodeOfType(nsINode::eTEXT)) { @@ -968,15 +954,15 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, f->GetRenderedText(&renderedWhitespace, nsnull, nsnull, 0, 1); if (renderedWhitespace.IsEmpty()) { // Really empty -- nothing is rendered - if (aIsHidden) - *aIsHidden = PR_TRUE; + if (aIsSubtreeHidden) + *aIsSubtreeHidden = true; return nsnull; } } if (weakFrame.IsAlive()) { newAcc = weakFrame.GetFrame()->CreateAccessible(); - if (InitAccessible(newAcc, nsnull)) + if (docAcc->BindToDocument(newAcc, nsnull)) return newAcc.forget(); return nsnull; } @@ -997,14 +983,14 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, nsAutoString name; content->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::name, name); if (!name.IsEmpty()) { - if (aIsHidden) - *aIsHidden = PR_TRUE; + if (aIsSubtreeHidden) + *aIsSubtreeHidden = true; return nsnull; } newAcc = new nsHyperTextAccessibleWrap(content, aWeakShell); - if (InitAccessible(newAcc, nsAccUtils::GetRoleMapEntry(aNode))) + if (docAcc->BindToDocument(newAcc, nsAccUtils::GetRoleMapEntry(aNode))) return newAcc.forget(); return nsnull; } @@ -1122,8 +1108,8 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, // captions. This could not be done in // nsTableCaptionFrame::GetAccessible() because the descendants of // the table caption would still be created. By setting - // *aIsHidden = PR_TRUE we ensure that no descendant accessibles are - // created. + // *aIsSubtreeHidden = true we ensure that no descendant accessibles + // are created. nsIFrame* f = weakFrame.GetFrame(); if (!f) { f = aPresShell->GetRealPrimaryFrameFor(content); @@ -1132,8 +1118,8 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, f->GetRect().IsEmpty()) { // XXX This is not the ideal place for this code, but right now there // is no better place: - if (aIsHidden) - *aIsHidden = PR_TRUE; + if (aIsSubtreeHidden) + *aIsSubtreeHidden = true; return nsnull; } @@ -1191,7 +1177,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, } } - if (InitAccessible(newAcc, roleMapEntry)) + if (docAcc->BindToDocument(newAcc, roleMapEntry)) return newAcc.forget(); return nsnull; } @@ -1348,22 +1334,29 @@ nsAccessibilityService::GetAreaAccessible(nsIFrame* aImageFrame, // Try to get image map accessible from the global cache or create it // if failed. - nsRefPtr imageAcc = - GetCachedAccessible(aImageFrame->GetContent(), aWeakShell); - if (!imageAcc) { - imageAcc = CreateHTMLImageAccessible(aImageFrame->GetContent(), - aImageFrame->PresContext()->PresShell()); + nsRefPtr image = GetCachedAccessible(aImageFrame->GetContent(), + aWeakShell); + if (!image) { + image = CreateHTMLImageAccessible(aImageFrame->GetContent(), + aImageFrame->PresContext()->PresShell()); - if (!InitAccessible(imageAcc, nsnull)) + nsDocAccessible* document = + GetAccService()->GetDocAccessible(aAreaNode->GetOwnerDoc()); + if (!document) { + NS_NOTREACHED("No document for accessible being created!"); + return nsnull; + } + + if (!document->BindToDocument(image, nsnull)) return nsnull; } if (aImageAccessible) - *aImageAccessible = imageAcc; + *aImageAccessible = image; // Make sure accessible children of the image map are cached so // that they should be available in global cache. - imageAcc->EnsureChildren(); + image->EnsureChildren(); return GetCachedAccessible(aAreaNode, aWeakShell); } @@ -1771,7 +1764,7 @@ nsAccessibilityService::AddNativeRootAccessible(void* aAtkAccessible) if (!applicationAcc) return nsnull; - nsNativeRootAccessibleWrap* nativeRootAcc = + nsRefPtr nativeRootAcc = new nsNativeRootAccessibleWrap((AtkObject*)aAtkAccessible); if (!nativeRootAcc) return nsnull; diff --git a/accessible/src/base/nsAccessibilityService.h b/accessible/src/base/nsAccessibilityService.h index d8f5eadc5be8..85033eeb7805 100644 --- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -111,9 +111,9 @@ public: virtual void RemoveNativeRootAccessible(nsAccessible* aRootAccessible); virtual void ContentRangeInserted(nsIPresShell* aPresShell, - nsIContent* aContainer, - nsIContent* aStartChild, - nsIContent* aEndChild); + nsIContent* aContainer, + nsIContent* aStartChild, + nsIContent* aEndChild); virtual void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aContainer, nsIContent* aChild); @@ -138,16 +138,16 @@ public: * Return an accessible for the given DOM node from the cache or create new * one. * - * @param aNode [in] the given node - * @param aPresShell [in] the pres shell of the node - * @param aWeakShell [in] the weak shell for the pres shell - * @param aIsHidden [out, optional] indicates whether the node's frame is - * hidden + * @param aNode [in] the given node + * @param aPresShell [in] the pres shell of the node + * @param aWeakShell [in] the weak shell for the pres shell + * @param aIsSubtreeHidden [out, optional] indicates whether the node's + * frame and its subtree is hidden */ already_AddRefed GetOrCreateAccessible(nsINode* aNode, nsIPresShell* aPresShell, nsIWeakReference* aWeakShell, - PRBool* aIsHidden = nsnull); + bool* aIsSubtreeHidden = nsnull); /** * Return an accessible for the given DOM node. @@ -202,19 +202,6 @@ public: GetCachedAccessibleOrContainer(aNode->GetNodeParent()) : nsnull; } - /** - * Initialize an accessible and cache it. The method should be called for - * every created accessible. - * - * @param aAccessible [in] accessible to initialize. - * @param aRoleMapEntry [in] the role map entry role the ARIA role or nsnull - * if none - * - * @return true if the accessible was initialized, otherwise false - */ - PRBool InitAccessible(nsAccessible *aAccessible, - nsRoleMapEntry *aRoleMapEntry); - protected: /** * Return an accessible for the DOM node in the given presentation shell if diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 8257b2299066..e0e0753bdb2f 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -2621,19 +2621,6 @@ nsAccessible::AppendTextTo(nsAString& aText, PRUint32 aStartOffset, PRUint32 aLe //////////////////////////////////////////////////////////////////////////////// // nsAccessNode public methods -PRBool -nsAccessible::Init() -{ - if (!nsAccessNodeWrap::Init()) - return PR_FALSE; - - nsDocAccessible* document = - GetAccService()->GetDocAccessible(mContent->GetOwnerDoc()); - NS_ASSERTION(document, "Cannot cache new nsAccessible!"); - - return document ? document->CacheAccessible(this) : PR_FALSE; -} - void nsAccessible::Shutdown() { @@ -2912,20 +2899,6 @@ nsAccessible::GetIndexOfEmbeddedChild(nsAccessible* aChild) return GetIndexOf(aChild); } -#ifdef DEBUG -PRBool -nsAccessible::IsInCache() -{ - nsDocAccessible *docAccessible = - GetAccService()->GetDocAccessible(mContent->GetOwnerDoc()); - if (docAccessible) - return docAccessible->GetCachedAccessibleByUniqueID(UniqueID()) ? PR_TRUE : PR_FALSE; - - return PR_FALSE; -} -#endif - - //////////////////////////////////////////////////////////////////////////////// // HyperLinkAccessible methods diff --git a/accessible/src/base/nsAccessible.h b/accessible/src/base/nsAccessible.h index c6f5560fb5cb..a01fbe3b7ef0 100644 --- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -113,7 +113,6 @@ public: ////////////////////////////////////////////////////////////////////////////// // nsAccessNode - virtual PRBool Init(); virtual void Shutdown(); ////////////////////////////////////////////////////////////////////////////// @@ -298,13 +297,6 @@ public: PRBool AreChildrenCached() const { return mChildrenFlags != eChildrenUninitialized; } bool IsBoundToParent() const { return mParent; } -#ifdef DEBUG - /** - * Return true if the access node is cached. - */ - PRBool IsInCache(); -#endif - ////////////////////////////////////////////////////////////////////////////// // Miscellaneous methods @@ -441,7 +433,7 @@ protected: /** * Set accessible parent and index in parent. */ - void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent); + virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent); void UnbindFromParent(); /** diff --git a/accessible/src/base/nsBaseWidgetAccessible.cpp b/accessible/src/base/nsBaseWidgetAccessible.cpp index 3024209dab50..9f3ba95f745b 100644 --- a/accessible/src/base/nsBaseWidgetAccessible.cpp +++ b/accessible/src/base/nsBaseWidgetAccessible.cpp @@ -207,13 +207,6 @@ nsLinkableAccessible::GetKeyboardShortcut(nsAString& aKeyboardShortcut) //////////////////////////////////////////////////////////////////////////////// // nsLinkableAccessible. nsAccessNode -PRBool -nsLinkableAccessible::Init() -{ - CacheActionContent(); - return nsAccessibleWrap::Init(); -} - void nsLinkableAccessible::Shutdown() { @@ -242,11 +235,19 @@ nsLinkableAccessible::GetAnchorURI(PRUint32 aAnchorIndex) } //////////////////////////////////////////////////////////////////////////////// -// nsLinkableAccessible +// nsLinkableAccessible: nsAccessible protected void -nsLinkableAccessible::CacheActionContent() +nsLinkableAccessible::BindToParent(nsAccessible* aParent, + PRUint32 aIndexInParent) { + nsAccessibleWrap::BindToParent(aParent, aIndexInParent); + + // Cache action content. + mActionContent = nsnull; + mIsLink = PR_FALSE; + mIsOnclick = PR_FALSE; + nsIContent* walkUpContent = mContent; PRBool isOnclick = nsCoreUtils::HasClickListener(walkUpContent); @@ -276,6 +277,9 @@ nsLinkableAccessible::CacheActionContent() } } +//////////////////////////////////////////////////////////////////////////////// +// nsLinkableAccessible: protected + nsAccessible * nsLinkableAccessible::GetActionAccessible() const { diff --git a/accessible/src/base/nsBaseWidgetAccessible.h b/accessible/src/base/nsBaseWidgetAccessible.h index 74245e034b16..7876c81b438b 100644 --- a/accessible/src/base/nsBaseWidgetAccessible.h +++ b/accessible/src/base/nsBaseWidgetAccessible.h @@ -97,7 +97,6 @@ public: NS_IMETHOD GetKeyboardShortcut(nsAString& _retval); // nsAccessNode - virtual PRBool Init(); virtual void Shutdown(); // nsAccessible @@ -107,16 +106,16 @@ public: virtual already_AddRefed GetAnchorURI(PRUint32 aAnchorIndex); protected: + // nsAccessible + virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent); + + // nsLinkableAccessible + /** * Return an accessible for cached action node. */ nsAccessible *GetActionAccessible() const; - /** - * Cache action node. - */ - virtual void CacheActionContent(); - nsCOMPtr mActionContent; PRPackedBool mIsLink; PRPackedBool mIsOnclick; diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index e4ec97bb1994..3016e0e9d34e 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -599,31 +599,6 @@ nsDocAccessible::GetCachedAccessible(nsINode *aNode) return accessible; } -// nsDocAccessible public method -PRBool -nsDocAccessible::CacheAccessible(nsAccessible* aAccessible) -{ - if (aAccessible->IsPrimaryForNode() && - !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible)) - return PR_FALSE; - - return mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible); -} - -// nsDocAccessible public method -void -nsDocAccessible::ShutdownAccessible(nsAccessible *aAccessible) -{ - // Remove an accessible from node to accessible map if it is presented there. - if (aAccessible->IsPrimaryForNode() && - mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible) - mNodeToAccessibleMap.Remove(aAccessible->GetNode()); - - void* uniqueID = aAccessible->UniqueID(); - aAccessible->Shutdown(); - mAccessibleCache.Remove(uniqueID); -} - //////////////////////////////////////////////////////////////////////////////// // nsAccessNode @@ -1049,8 +1024,21 @@ nsDocAccessible::AttributeChangedImpl(nsIContent* aContent, PRInt32 aNameSpaceID } } - if (aAttribute == nsAccessibilityAtoms::role || - aAttribute == nsAccessibilityAtoms::href || + if (aAttribute == nsAccessibilityAtoms::role) { + if (mContent == aContent) { + // It is common for js libraries to set the role of the body element after + // the doc has loaded. In this case we just update the role map entry. + SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(aContent)); + } + else { + // Recreate the accessible when role is changed because we might require a + // different accessible class for the new role or the accessible may + // expose a different sets of interfaces (COM restriction). + RecreateAccessible(aContent); + } + } + + if (aAttribute == nsAccessibilityAtoms::href || aAttribute == nsAccessibilityAtoms::onclick) { // Not worth the expense to ensure which namespace these are in // It doesn't kill use to recreate the accessible even if the attribute was used @@ -1345,6 +1333,56 @@ nsDocAccessible::GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID) return nsnull; } +bool +nsDocAccessible::BindToDocument(nsAccessible* aAccessible, + nsRoleMapEntry* aRoleMapEntry) +{ + if (!aAccessible) + return false; + + // Put into DOM node cache. + if (aAccessible->IsPrimaryForNode() && + !mNodeToAccessibleMap.Put(aAccessible->GetNode(), aAccessible)) + return false; + + // Put into unique ID cache. + if (!mAccessibleCache.Put(aAccessible->UniqueID(), aAccessible)) { + if (aAccessible->IsPrimaryForNode()) + mNodeToAccessibleMap.Remove(aAccessible->GetNode()); + + return false; + } + + // Initialize the accessible. + if (!aAccessible->Init()) { + NS_ERROR("Failed to initialize an accessible!"); + + UnbindFromDocument(aAccessible); + return false; + } + + aAccessible->SetRoleMapEntry(aRoleMapEntry); + return true; +} + +void +nsDocAccessible::UnbindFromDocument(nsAccessible* aAccessible) +{ + // Remove an accessible from node-to-accessible map if it exists there. + if (aAccessible->IsPrimaryForNode() && + mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible) + mNodeToAccessibleMap.Remove(aAccessible->GetNode()); + +#ifdef DEBUG + NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()), + "Unbinding the unbound accessible!"); +#endif + + void* uniqueID = aAccessible->UniqueID(); + aAccessible->Shutdown(); + mAccessibleCache.Remove(uniqueID); +} + void nsDocAccessible::UpdateTree(nsIContent* aContainerNode, nsIContent* aStartNode, @@ -1761,10 +1799,6 @@ nsDocAccessible::UncacheChildrenInSubtree(nsAccessible* aRoot) void nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible) { -#ifdef DEBUG - nsAccessible* incache = mAccessibleCache.GetWeak(aAccessible->UniqueID()); -#endif - // Traverse through children and shutdown them before this accessible. When // child gets shutdown then it removes itself from children array of its //parent. Use jdx index to process the cases if child is not attached to the @@ -1780,6 +1814,6 @@ nsDocAccessible::ShutdownChildrenInSubtree(nsAccessible* aAccessible) ShutdownChildrenInSubtree(child); } - ShutdownAccessible(aAccessible); + UnbindFromDocument(aAccessible); } diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index 5c6789747643..7ea913d3687c 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -211,18 +211,18 @@ public: nsAccessible* GetCachedAccessibleByUniqueIDInSubtree(void* aUniqueID); /** - * Cache the accessible. + * Initialize the newly created accessible and put it into document caches. * - * @param aAccessible [in] accessible to cache - * - * @return true if accessible being cached, otherwise false + * @param aAccessible [in] created accessible + * @param aRoleMapEntry [in] the role map entry role the ARIA role or nsnull + * if none */ - PRBool CacheAccessible(nsAccessible *aAccessible); + bool BindToDocument(nsAccessible* aAccessible, nsRoleMapEntry* aRoleMapEntry); /** - * Shutdown the accessible and remove it from document cache. + * Remove from document and shutdown the given accessible. */ - void ShutdownAccessible(nsAccessible *aAccessible); + void UnbindFromDocument(nsAccessible* aAccessible); /** * Process the event when the queue of pending events is untwisted. Fire diff --git a/accessible/src/base/nsTextEquivUtils.cpp b/accessible/src/base/nsTextEquivUtils.cpp index dc22b2e8b304..0abdb8073c48 100644 --- a/accessible/src/base/nsTextEquivUtils.cpp +++ b/accessible/src/base/nsTextEquivUtils.cpp @@ -237,11 +237,6 @@ nsresult nsTextEquivUtils::AppendFromAccessible(nsAccessible *aAccessible, nsAString *aString) { - // Ignore hidden accessible for name computation. - nsIFrame* frame = aAccessible->GetFrame(); - if (!frame || !frame->GetStyleVisibility()->IsVisible()) - return NS_OK; - //XXX: is it necessary to care the accessible is not a document? if (aAccessible->IsContent()) { nsresult rv = AppendTextEquivFromTextContent(aAccessible->GetContent(), diff --git a/accessible/src/html/nsHTMLImageMapAccessible.cpp b/accessible/src/html/nsHTMLImageMapAccessible.cpp index c73839002b97..79753056523d 100644 --- a/accessible/src/html/nsHTMLImageMapAccessible.cpp +++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp @@ -40,6 +40,7 @@ #include "nsHTMLImageMapAccessible.h" #include "nsAccUtils.h" +#include "nsDocAccessible.h" #include "nsIDOMHTMLCollection.h" #include "nsIServiceManager.h" @@ -114,6 +115,8 @@ nsHTMLImageMapAccessible::CacheChildren() if (!mapAreas) return; + nsDocAccessible* document = GetDocAccessible(); + PRUint32 areaCount = 0; mapAreas->GetLength(&areaCount); @@ -124,21 +127,13 @@ nsHTMLImageMapAccessible::CacheChildren() return; nsCOMPtr areaContent(do_QueryInterface(areaNode)); - nsRefPtr areaAcc = + nsRefPtr area = new nsHTMLAreaAccessible(areaContent, mWeakShell); - if (!areaAcc) - return; - if (!areaAcc->Init()) { - areaAcc->Shutdown(); + if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) || + !AppendChild(area)) { return; } - - // We must respect ARIA on area elements (for the canvas map technique) - areaAcc->SetRoleMapEntry(nsAccUtils::GetRoleMapEntry(areaContent)); - - if (!AppendChild(areaAcc)) - return; } } diff --git a/accessible/src/html/nsHTMLSelectAccessible.cpp b/accessible/src/html/nsHTMLSelectAccessible.cpp index 266507f7f901..14a468c10b9c 100644 --- a/accessible/src/html/nsHTMLSelectAccessible.cpp +++ b/accessible/src/html/nsHTMLSelectAccessible.cpp @@ -692,21 +692,17 @@ nsHTMLComboboxAccessible::CacheChildren() if (!mListAccessible) { mListAccessible = new nsHTMLComboboxListAccessible(mParent, mContent, mWeakShell); - if (!mListAccessible) - return; // Initialize and put into cache. - if (!mListAccessible->Init()) { - mListAccessible->Shutdown(); + if (!GetDocAccessible()->BindToDocument(mListAccessible, nsnull)) return; - } } - AppendChild(mListAccessible); - - // Cache combobox option accessibles so that we build complete accessible tree - // for combobox. - mListAccessible->EnsureChildren(); + if (AppendChild(mListAccessible)) { + // Cache combobox option accessibles so that we build complete accessible + // tree for combobox. + mListAccessible->EnsureChildren(); + } } void diff --git a/accessible/src/html/nsHTMLTextAccessible.cpp b/accessible/src/html/nsHTMLTextAccessible.cpp index 3804e3c450a4..d3e5f82ceef5 100644 --- a/accessible/src/html/nsHTMLTextAccessible.cpp +++ b/accessible/src/html/nsHTMLTextAccessible.cpp @@ -259,8 +259,7 @@ nsHTMLLIAccessible:: nsBlockFrame* blockFrame = do_QueryFrame(GetFrame()); if (blockFrame && !blockFrame->BulletIsEmptyExternal()) { mBulletAccessible = new nsHTMLListBulletAccessible(mContent, mWeakShell); - if (mBulletAccessible) - mBulletAccessible->Init(); + GetDocAccessible()->BindToDocument(mBulletAccessible, nsnull); } } diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp index 3fa396be68b3..8481258fc753 100644 --- a/accessible/src/html/nsHyperTextAccessible.cpp +++ b/accessible/src/html/nsHyperTextAccessible.cpp @@ -155,6 +155,9 @@ nsHyperTextAccessible::NativeRole() if (tag == nsAccessibilityAtoms::footer) return nsIAccessibleRole::ROLE_FOOTER; + if (tag == nsAccessibilityAtoms::aside) + return nsIAccessibleRole::ROLE_NOTE; + // Treat block frames as paragraphs nsIFrame *frame = GetFrame(); if (frame && frame->GetType() == nsAccessibilityAtoms::blockFrame && @@ -1221,6 +1224,9 @@ nsHyperTextAccessible::GetAttributesInternal(nsIPersistentProperties *aAttribute else if (mContent->Tag() == nsAccessibilityAtoms::article) nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles, NS_LITERAL_STRING("main")); + else if (mContent->Tag() == nsAccessibilityAtoms::aside) + nsAccUtils::SetAccAttr(aAttributes, nsAccessibilityAtoms::xmlroles, + NS_LITERAL_STRING("note")); return NS_OK; } diff --git a/accessible/src/mac/nsRoleMap.h b/accessible/src/mac/nsRoleMap.h index 77fdb96bbf51..664087bc987d 100644 --- a/accessible/src/mac/nsRoleMap.h +++ b/accessible/src/mac/nsRoleMap.h @@ -165,5 +165,6 @@ static const NSString* AXRoles [] = { NSAccessibilityUnknownRole, // ROLE_FLAT_EQUATION NSAccessibilityGroupRole, // ROLE_GRID_CELL NSAccessibilityGroupRole, // ROLE_EMBEDDED_OBJECT + NSAccessibilityGroupRole, // ROLE_NOTE @"ROLE_LAST_ENTRY" // ROLE_LAST_ENTRY. bogus role that will never be shown (just marks the end of this array)! }; diff --git a/accessible/src/msaa/nsRoleMap.h b/accessible/src/msaa/nsRoleMap.h index cfb9ff4e1897..bede11d93f66 100644 --- a/accessible/src/msaa/nsRoleMap.h +++ b/accessible/src/msaa/nsRoleMap.h @@ -443,6 +443,9 @@ static const WindowsRoleMapItem gWindowsRoleMap[] = { // nsIAccessibleRole::ROLE_EMBEDDED_OBJECT { USE_ROLE_STRING, IA2_ROLE_EMBEDDED_OBJECT }, + // nsIAccessibleRole::ROLE_NOTE + { USE_ROLE_STRING, IA2_ROLE_NOTE }, + // nsIAccessibleRole::ROLE_LAST_ENTRY { ROLE_WINDOWS_LAST_ENTRY, ROLE_WINDOWS_LAST_ENTRY } }; diff --git a/accessible/src/xul/nsXULColorPickerAccessible.cpp b/accessible/src/xul/nsXULColorPickerAccessible.cpp index b5792cb0f3b6..87e41858a38f 100644 --- a/accessible/src/xul/nsXULColorPickerAccessible.cpp +++ b/accessible/src/xul/nsXULColorPickerAccessible.cpp @@ -41,6 +41,7 @@ #include "nsAccUtils.h" #include "nsAccTreeWalker.h" #include "nsCoreUtils.h" +#include "nsDocAccessible.h" #include "nsIDOMElement.h" @@ -171,5 +172,8 @@ nsXULColorPickerAccessible::CacheChildren() AppendChild(child); return; } + + // Unbind rejected accessibles from the document. + GetDocAccessible()->UnbindFromDocument(child); } } diff --git a/accessible/src/xul/nsXULFormControlAccessible.cpp b/accessible/src/xul/nsXULFormControlAccessible.cpp index 9106507e4268..1f0375a5bbba 100644 --- a/accessible/src/xul/nsXULFormControlAccessible.cpp +++ b/accessible/src/xul/nsXULFormControlAccessible.cpp @@ -43,6 +43,7 @@ #include "nsAccUtils.h" #include "nsAccTreeWalker.h" #include "nsCoreUtils.h" +#include "nsDocAccessible.h" #include "nsRelUtils.h" // NOTE: alphabetically ordered @@ -222,6 +223,10 @@ nsXULButtonAccessible::CacheChildren() // for it. Ignore dropmarker button what is placed as a last child. buttonAccessible.swap(child); break; + + } else { + // Unbind rejected accessible from document. + GetDocAccessible()->UnbindFromDocument(child); } } diff --git a/accessible/src/xul/nsXULTreeAccessible.cpp b/accessible/src/xul/nsXULTreeAccessible.cpp index ca5c06e5a8bd..cb323de1a0c3 100644 --- a/accessible/src/xul/nsXULTreeAccessible.cpp +++ b/accessible/src/xul/nsXULTreeAccessible.cpp @@ -472,23 +472,21 @@ nsXULTreeAccessible::GetTreeItemAccessible(PRInt32 aRow) return nsnull; void *key = reinterpret_cast(aRow); - nsRefPtr accessible = mAccessibleCache.GetWeak(key); + nsAccessible* cachedTreeItem = mAccessibleCache.GetWeak(key); + if (cachedTreeItem) + return cachedTreeItem; - if (!accessible) { - accessible = CreateTreeItemAccessible(aRow); - if (!accessible) - return nsnull; + nsRefPtr treeItem = CreateTreeItemAccessible(aRow); + if (treeItem) { + if (mAccessibleCache.Put(key, treeItem)) { + if (GetDocAccessible()->BindToDocument(treeItem, nsnull)) + return treeItem; - if (!accessible->Init()) { - accessible->Shutdown(); - return nsnull; + mAccessibleCache.Remove(key); } - - if (!mAccessibleCache.Put(key, accessible)) - return nsnull; } - return accessible; + return nsnull; } void @@ -501,22 +499,21 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount) if (aCount > 0) return; + nsDocAccessible* document = GetDocAccessible(); + // Fire destroy event for removed tree items and delete them from caches. for (PRInt32 rowIdx = aRow; rowIdx < aRow - aCount; rowIdx++) { void* key = reinterpret_cast(rowIdx); - nsAccessible *accessible = mAccessibleCache.GetWeak(key); + nsAccessible* treeItem = mAccessibleCache.GetWeak(key); - if (accessible) { + if (treeItem) { nsRefPtr event = - new AccEvent(nsIAccessibleEvent::EVENT_HIDE, accessible); + new AccEvent(nsIAccessibleEvent::EVENT_HIDE, treeItem); nsEventShell::FireEvent(event); - // Shutdown and remove accessible from document cache and tree cache. - nsDocAccessible *docAccessible = GetDocAccessible(); - if (docAccessible) - docAccessible->ShutdownAccessible(accessible); - + // Unbind from document, shutdown and remove from tree cache. + document->UnbindFromDocument(treeItem); mAccessibleCache.Remove(key); } } @@ -534,14 +531,11 @@ nsXULTreeAccessible::InvalidateCache(PRInt32 aRow, PRInt32 aCount) for (PRInt32 rowIdx = newRowCount; rowIdx < oldRowCount; ++rowIdx) { void *key = reinterpret_cast(rowIdx); - nsAccessible *accessible = mAccessibleCache.GetWeak(key); - - if (accessible) { - // Shutdown and remove accessible from document cache and tree cache. - nsDocAccessible *docAccessible = GetDocAccessible(); - if (docAccessible) - docAccessible->ShutdownAccessible(accessible); + nsAccessible* treeItem = mAccessibleCache.GetWeak(key); + if (treeItem) { + // Unbind from document, shutdown and remove from tree cache. + document->UnbindFromDocument(treeItem); mAccessibleCache.Remove(key); } } diff --git a/accessible/src/xul/nsXULTreeGridAccessible.cpp b/accessible/src/xul/nsXULTreeGridAccessible.cpp index a91709e13f5d..5450410e28bd 100644 --- a/accessible/src/xul/nsXULTreeGridAccessible.cpp +++ b/accessible/src/xul/nsXULTreeGridAccessible.cpp @@ -41,6 +41,7 @@ #include "nsAccCache.h" #include "nsAccessibilityService.h" #include "nsAccUtils.h" +#include "nsDocAccessible.h" #include "nsEventShell.h" #include "nsITreeSelection.h" @@ -729,25 +730,23 @@ nsXULTreeGridRowAccessible::GetCellAccessible(nsITreeColumn* aColumn) NS_PRECONDITION(aColumn, "No tree column!"); void* key = static_cast(aColumn); - nsRefPtr accessible = mAccessibleCache.GetWeak(key); + nsAccessible* cachedCell = mAccessibleCache.GetWeak(key); + if (cachedCell) + return cachedCell; - if (!accessible) { - accessible = - new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree, - mTreeView, mRow, aColumn); - if (!accessible) - return nsnull; + nsRefPtr cell = + new nsXULTreeGridCellAccessibleWrap(mContent, mWeakShell, this, mTree, + mTreeView, mRow, aColumn); + if (cell) { + if (mAccessibleCache.Put(key, cell)) { + if (GetDocAccessible()->BindToDocument(cell, nsnull)) + return cell; - if (!accessible->Init()) { - accessible->Shutdown(); - return nsnull; + mAccessibleCache.Remove(key); } - - if (!mAccessibleCache.Put(key, accessible)) - return nsnull; } - return accessible; + return nsnull; } void diff --git a/accessible/tests/mochitest/Makefile.in b/accessible/tests/mochitest/Makefile.in index 106947b01118..64a3aac7c3d8 100644 --- a/accessible/tests/mochitest/Makefile.in +++ b/accessible/tests/mochitest/Makefile.in @@ -51,6 +51,7 @@ DIRS = \ selectable \ states \ table \ + text \ tree \ treeupdate \ $(null) @@ -121,6 +122,7 @@ _TEST_FILES =\ test_value.html \ test_value.xul \ testTextboxes.js \ + text.js \ treeview.css \ treeview.js \ $(NULL) diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 8118ef0f4d91..0c6de0d2b1d4 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -320,27 +320,41 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree) if (!acc) return; - for (var prop in aAccTree) { + var accTree = aAccTree; + + // Support of simplified accessible tree object. + var key = Object.keys(accTree)[0]; + var roleName = "ROLE_" + key; + if (roleName in nsIAccessibleRole) { + accTree = { + role: nsIAccessibleRole[roleName], + children: accTree[key] + }; + } + + // Test accessible properties. + for (var prop in accTree) { var msg = "Wrong value of property '" + prop + "' for " + prettyName(acc) + "."; if (prop == "role") { - is(roleToString(acc[prop]), roleToString(aAccTree[prop]), msg); + is(roleToString(acc[prop]), roleToString(accTree[prop]), msg); } else if (prop == "states") { - var statesObj = aAccTree[prop]; + var statesObj = accTree[prop]; testStates(acc, statesObj.states, statesObj.extraStates, statesObj.absentStates, statesObj.absentExtraStates); } else if (prop != "children") { - is(acc[prop], aAccTree[prop], msg); + is(acc[prop], accTree[prop], msg); } } - if ("children" in aAccTree && aAccTree["children"] instanceof Array) { + // Test children. + if ("children" in accTree && accTree["children"] instanceof Array) { var children = acc.children; - is(children.length, aAccTree.children.length, + is(children.length, accTree.children.length, "Different amount of expected children of " + prettyName(acc) + "."); - if (aAccTree.children.length == children.length) { + if (accTree.children.length == children.length) { var childCount = children.length; // nsIAccessible::firstChild @@ -390,7 +404,7 @@ function testAccessibleTree(aAccOrElmOrID, aAccTree) "Wrong previous sibling of " + prettyName(child)); // Go down through subtree - testAccessibleTree(child, aAccTree.children[i]); + testAccessibleTree(child, accTree.children[i]); } } } diff --git a/accessible/tests/mochitest/events/test_mutation.html b/accessible/tests/mochitest/events/test_mutation.html index d0d2c74c79aa..4b0bcf804336 100644 --- a/accessible/tests/mochitest/events/test_mutation.html +++ b/accessible/tests/mochitest/events/test_mutation.html @@ -293,6 +293,13 @@ gQueue.push(new changeStyle(id, "display", "none", kHideEvents)); gQueue.push(new changeStyle(id, "display", "inline", kShowEvents)); + // Show/hide events by changing of visibility style of accessible DOM node + // from 'visible' to 'hidden', 'hidden' to 'visible'. + var id = "link2"; + getAccessible(id); + gQueue.push(new changeStyle(id, "visibility", "hidden", kHideEvents)); + gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents)); + // Show/hide events by changing of display style of accessible DOM node // from 'inline' to 'block', 'block' to 'inline'. var id = "link3"; @@ -300,6 +307,12 @@ gQueue.push(new changeStyle(id, "display", "block", kHideAndShowEvents)); gQueue.push(new changeStyle(id, "display", "inline", kHideAndShowEvents)); + // Show/hide events by changing of visibility style of accessible DOM node + // from 'collapse' to 'visible', 'visible' to 'collapse'. + var id = "link4"; + gQueue.push(new changeStyle(id, "visibility", "visible", kShowEvents)); + gQueue.push(new changeStyle(id, "visibility", "collapse", kHideEvents)); + // Show/hide events by adding new accessible DOM node and removing old one. var id = "link5"; gQueue.push(new cloneAndAppendToDOM(id)); @@ -343,6 +356,10 @@ gQueue.push(new changeClass("container2", "link7", "displayNone", kHideEvents)); + gQueue.push(new changeClass("container3", "link8", "", kShowEvents)); + gQueue.push(new changeClass("container3", "link8", "visibilityHidden", + kHideEvents)); + gQueue.invoke(); // Will call SimpleTest.finish(); } @@ -367,6 +384,11 @@ title="Rework accessible tree update code" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570275"> Mozilla Bug 570275 +
+ + Mozilla Bug 606125

diff --git a/accessible/tests/mochitest/role.js b/accessible/tests/mochitest/role.js index 021e536eb0b5..639cd7fb46b3 100644 --- a/accessible/tests/mochitest/role.js +++ b/accessible/tests/mochitest/role.js @@ -37,6 +37,7 @@ const ROLE_LISTITEM = nsIAccessibleRole.ROLE_LISTITEM; const ROLE_MENUITEM = nsIAccessibleRole.ROLE_MENUITEM; const ROLE_MENUPOPUP = nsIAccessibleRole.ROLE_MENUPOPUP; const ROLE_NOTHING = nsIAccessibleRole.ROLE_NOTHING; +const ROLE_NOTE = nsIAccessibleRole.ROLE_NOTE; const ROLE_OPTION = nsIAccessibleRole.ROLE_OPTION; const ROLE_OUTLINE = nsIAccessibleRole.ROLE_OUTLINE; const ROLE_OUTLINEITEM = nsIAccessibleRole.ROLE_OUTLINEITEM; diff --git a/accessible/tests/mochitest/test_aria_roles.html b/accessible/tests/mochitest/test_aria_roles.html index 4716e2007bd9..cdab67e4c4e6 100644 --- a/accessible/tests/mochitest/test_aria_roles.html +++ b/accessible/tests/mochitest/test_aria_roles.html @@ -71,6 +71,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289 testRole("scrollbar", ROLE_SCROLLBAR); testRole("dir", ROLE_LIST); + // test document role map update + var testDoc = getAccessible(document, [nsIAccessibleDocument]); + testRole(testDoc, ROLE_DOCUMENT); + document.body.setAttribute("role", "application"); + testRole(testDoc, ROLE_APPLICATION); + SimpleTest.finish(); } @@ -84,6 +90,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289 Mozilla Bug 469688 Mozilla Bug 520188 Mozilla Bug 529289 + Mozilla Bug 607219

diff --git a/accessible/tests/mochitest/test_elm_landmarks.html b/accessible/tests/mochitest/test_elm_landmarks.html index d1556caedcd3..8f42ec370f51 100644 --- a/accessible/tests/mochitest/test_elm_landmarks.html +++ b/accessible/tests/mochitest/test_elm_landmarks.html @@ -25,12 +25,14 @@ testRole("header", ROLE_HEADER); testRole("footer", ROLE_FOOTER); testRole("article", ROLE_SECTION); + testRole("aside", ROLE_NOTE); // Some AT may look for this testAttrs("nav", {"xml-roles" : "navigation"}, true); testAttrs("header", {"xml-roles" : "banner"}, true); testAttrs("footer", {"xml-roles" : "contentinfo"}, true); testAttrs("article", {"xml-roles" : "main"}, true); + testAttrs("aside", {"xml-roles" : "note"}, true); testAttrs("document", {"xml-roles" : "document"}, true); // ARIA override // And some AT may look for this @@ -38,6 +40,7 @@ testAttrs("header", {"tag" : "HEADER"}, true); testAttrs("footer", {"tag" : "FOOTER"}, true); testAttrs("article", {"tag" : "ARTICLE"}, true); + testAttrs("aside", {"tag" : "ASIDE"}, true); testAttrs("document", {"tag" : "ARTICLE"}, true); // no override expected SimpleTest.finish(); @@ -61,6 +64,7 @@
a footer
an article
+
a document
diff --git a/accessible/tests/mochitest/text.js b/accessible/tests/mochitest/text.js new file mode 100644 index 000000000000..ef23308f9748 --- /dev/null +++ b/accessible/tests/mochitest/text.js @@ -0,0 +1,180 @@ +//////////////////////////////////////////////////////////////////////////////// +// Public + +const BOUNDARY_CHAR = nsIAccessibleText.BOUNDARY_CHAR; +const BOUNDARY_WORD_START = nsIAccessibleText.BOUNDARY_WORD_START; +const BOUNDARY_WORD_END = nsIAccessibleText.BOUNDARY_WORD_END; +const BOUNDARY_LINE_START = nsIAccessibleText.BOUNDARY_LINE_START; +const BOUNDARY_LINE_END = nsIAccessibleText.BOUNDARY_LINE_END; +const BOUNDARY_ATTRIBUTE_RANGE = nsIAccessibleText.BOUNDARY_ATTRIBUTE_RANGE; + +const kTodo = 1; +const kOk = 2; + +function testText(aIDs, aStartOffset, aEndOffset, aText) +{ + for (var i = 0; i < aIDs.length; i++) + { + var acc = getAccessible(aIDs[i], nsIAccessibleText); + try { + is(acc.getText(aStartOffset, aEndOffset), aText, + "getText: wrong text between start and end offsets '" + aStartOffset + + "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'"); + } catch (e) { + ok(false, + "getText fails between start and end offsets '" + aStartOffset + + "', '" + aEndOffset + " for '" + prettyName(aIDs[i]) + "'"); + } + } +} + +/** + * Test getTextAtOffset function over different elements + * + * @param aOffset [in] the offset to get the text at + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextAtOffset + * @param aStartOffset [in] expected return start offset for getTextAtOffset + * @param aEndOffset [in] expected return end offset for getTextAtOffset + * @param ... [in] list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + * + */ +function testTextAtOffset(aOffset, aBoundaryType, aText, + aStartOffset, aEndOffset) +{ + for (var i = 5; i < arguments.length; i = i + 4) { + var ID = arguments[i]; + var acc = getAccessible(ID, nsIAccessibleText); + var toDoFlag1 = arguments[i + 1]; + var toDoFlag2 = arguments[i + 2]; + var toDoFlag3 = arguments[i + 3]; + + testTextHelper(ID, aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset, + toDoFlag1, toDoFlag2, toDoFlag3, + acc.getTextAtOffset, "getTextAtOffset "); + } +} + +/** + * Test getTextAfterOffset function over different elements + * + * @param aOffset [in] the offset to get the text after + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextAfterOffset + * @param aStartOffset [in] expected return start offset for getTextAfterOffset + * @param aEndOffset [in] expected return end offset for getTextAfterOffset + * @param ... [in] list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + * + */ +function testTextAfterOffset(aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset) +{ + for (var i = 5; i < arguments.length; i = i + 4) { + var ID = arguments[i]; + var acc = getAccessible(ID, nsIAccessibleText); + var toDoFlag1 = arguments[i + 1]; + var toDoFlag2 = arguments[i + 2]; + var toDoFlag3 = arguments[i + 3]; + + testTextHelper(ID, aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset, + toDoFlag1, toDoFlag2, toDoFlag3, + acc.getTextAfterOffset, "getTextAfterOffset "); + } +} + +/** + * Test getTextBeforeOffset function over different elements + * + * @param aOffset [in] the offset to get the text before + * @param aBoundaryType [in] Boundary type for text to be retrieved + * @param aText [in] expected return text for getTextBeforeOffset + * @param aStartOffset [in] expected return start offset for getTextBeforeOffset + * @param aEndOffset [in] expected return end offset for getTextBeforeOffset + * @param ... [in] list of tuples made of: + * element identifier + * kTodo or kOk for returned text + * kTodo or kOk for returned start offset + * kTodo or kOk for returned offset result + * + */ +function testTextBeforeOffset(aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset) +{ + for (var i = 5; i < arguments.length; i = i + 4) { + var ID = arguments[i]; + var acc = getAccessible(ID, nsIAccessibleText); + var toDoFlag1 = arguments[i + 1]; + var toDoFlag2 = arguments[i + 2]; + var toDoFlag3 = arguments[i + 3]; + + testTextHelper(ID, aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset, + toDoFlag1, toDoFlag2, toDoFlag3, + acc.getTextBeforeOffset, "getTextBeforeOffset "); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Private + +function testTextHelper(aID, aOffset, aBoundaryType, + aText, aStartOffset, aEndOffset, + aToDoFlag1, aToDoFlag2, aToDoFlag3, + aTextFunc, aTextFuncName) +{ + var exceptionFlag = aToDoFlag1 == undefined || + aToDoFlag2 == undefined || + aToDoFlag3 == undefined; + try { + var startOffsetObj = {}, endOffsetObj = {}; + var text = aTextFunc(aOffset, aBoundaryType, + startOffsetObj, endOffsetObj); + + var isFunc1 = (aToDoFlag1 == kTodo) ? todo_is : is; + var isFunc2 = (aToDoFlag2 == kTodo) ? todo_is : is; + var isFunc3 = (aToDoFlag3 == kTodo) ? todo_is : is; + + var startMsg = aTextFuncName + "(" + boundaryToString(aBoundaryType) + "): "; + + var endMsg = ", id: '" + prettyName(aID) + "';"; + + isFunc1(text, aText, + startMsg + "wrong text, offset: " + aOffset + endMsg); + isFunc2(startOffsetObj.value, aStartOffset, + startMsg + "wrong start offset, offset: " + aOffset + endMsg); + isFunc3(endOffsetObj.value, aEndOffset, + startMsg + "wrong end offset, offset: " + aOffset + endMsg); + + } catch (e) { + var okFunc = exceptionFlag ? todo : ok; + okFunc(false, startMsg + "failed at offset " + aOffset + endMsg); + } +} + +function boundaryToString(aBoundaryType) +{ + switch (aBoundaryType) { + case BOUNDARY_CHAR: + return "char"; + case BOUNDARY_WORD_START: + return "word start"; + case BOUNDARY_WORD_END: + return "word end"; + case BOUNDARY_LINE_START: + return "line start"; + case BOUNDARY_LINE_END: + return "line end"; + case BOUNDARY_ATTRIBUTE_RANGE: + return "attr range"; + } +} diff --git a/accessible/tests/mochitest/text/Makefile.in b/accessible/tests/mochitest/text/Makefile.in new file mode 100644 index 000000000000..5141d8fc6c45 --- /dev/null +++ b/accessible/tests/mochitest/text/Makefile.in @@ -0,0 +1,53 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2010 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Fernando Herrera (original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = accessible/text + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TEST_FILES = \ + test_singleline.html \ + $(NULL) + +libs:: $(_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir) diff --git a/accessible/tests/mochitest/text/test_singleline.html b/accessible/tests/mochitest/text/test_singleline.html new file mode 100644 index 000000000000..342b75290de1 --- /dev/null +++ b/accessible/tests/mochitest/text/test_singleline.html @@ -0,0 +1,574 @@ + + + + nsIAccessibleText getText related function tests for html:input,html:div and html:textarea + + + + + + + + + + + Mozilla Bug 452769 +

+ +
+  
+ + +
hello my friend
+
hello my friend
+ + + + diff --git a/accessible/tests/mochitest/tree/test_tabbrowser.xul b/accessible/tests/mochitest/tree/test_tabbrowser.xul index dde94d357505..56ef5bd1a083 100644 --- a/accessible/tests/mochitest/tree/test_tabbrowser.xul +++ b/accessible/tests/mochitest/tree/test_tabbrowser.xul @@ -70,9 +70,6 @@ var tabsAccTree = { role: ROLE_PAGETABLIST, children: [ - { - role: ROLE_PUSHBUTTON // tab scroll up button - }, { role: ROLE_PAGETAB, children: [ @@ -91,9 +88,6 @@ }, { role: ROLE_PUSHBUTTON - }, - { - role: ROLE_PUSHBUTTON // tab scroll down button } ] }; diff --git a/accessible/tests/mochitest/treeupdate/Makefile.in b/accessible/tests/mochitest/treeupdate/Makefile.in index d7010d782fe4..3c6ad87c89d9 100644 --- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -46,11 +46,13 @@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES =\ + test_ariadialog.html \ test_doc.html \ test_list_editabledoc.html \ test_list.html \ test_recreation.html \ - test_tableinsubtree.html \ + test_textleaf.html \ + test_visibility.html \ $(NULL) libs:: $(_TEST_FILES) diff --git a/accessible/tests/mochitest/treeupdate/test_tableinsubtree.html b/accessible/tests/mochitest/treeupdate/test_ariadialog.html similarity index 77% rename from accessible/tests/mochitest/treeupdate/test_tableinsubtree.html rename to accessible/tests/mochitest/treeupdate/test_ariadialog.html index 7d1e895c3ec2..4bde61a2bbcb 100644 --- a/accessible/tests/mochitest/treeupdate/test_tableinsubtree.html +++ b/accessible/tests/mochitest/treeupdate/test_ariadialog.html @@ -34,9 +34,10 @@ this.invoke = function showARIADialog_invoke() { - this.node.style.display = "block"; + getNode("dialog").style.display = "block"; + getNode("table").style.visibility = "visible"; + getNode("a").textContent = "link"; getNode("input").value = "hello"; - getNode("cell").textContent = "cell1"; getNode("input").focus(); } @@ -46,22 +47,11 @@ role: ROLE_DIALOG, children: [ { - role: ROLE_TABLE, - children: [ - { - role: ROLE_ROW, - children: [ - { - role: ROLE_CELL, - children: [ { role: ROLE_TEXT_LEAF } ] - }, - { - role: ROLE_CELL, - children: [ { role: ROLE_ENTRY } ] - } - ] - } - ] + role: ROLE_PUSHBUTTON, + children: [ { role: ROLE_TEXT_LEAF } ] + }, + { + role: ROLE_ENTRY } ] }; @@ -110,8 +100,18 @@ diff --git a/accessible/tests/mochitest/treeupdate/test_textleaf.html b/accessible/tests/mochitest/treeupdate/test_textleaf.html new file mode 100644 index 000000000000..db7c2428141a --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_textleaf.html @@ -0,0 +1,138 @@ + + + + + Test accessible recreation + + + + + + + + + + + + + + + + Mozilla Bug 545465 + + +

+ +
+  
+ +
+
div
+ span +
+ +
+ + diff --git a/accessible/tests/mochitest/treeupdate/test_visibility.html b/accessible/tests/mochitest/treeupdate/test_visibility.html new file mode 100644 index 000000000000..ec5a73b92c09 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_visibility.html @@ -0,0 +1,439 @@ + + + + + Style visibility tree update test + + + + + + + + + + + + + + + + Mozilla Bug 606125 + + +

+ +
+  
+ + +
+
+
text
+
+
+ + +
+
+
+
text
+
text
+
+
+
+ + +
+
+
text
+
+
+
text
+
+
+ + +
+ + + + +
+ +
+
+ + +
+
+ + + + +
+
text
+
+
+
+ + +
+
+ + + + +
+ + + + +
+
text
+
+
+
text
+
+
+ +
+ + diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index 43eab50cf34f..d55bee0511e2 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -93,6 +93,7 @@ endif CPPSRCS = nsBrowserApp.cpp LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre +LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/base ifdef BUILD_STATIC_LIBS ifdef _MSC_VER diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 1ea69b307c5a..93509cb81ec1 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -475,21 +475,6 @@ pref("privacy.sanitize.migrateFx3Prefs", false); pref("network.proxy.share_proxy_settings", false); // use the same proxy settings for all protocols -// l12n and i18n -pref("intl.accept_languages", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.static", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.more1", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.more2", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.more3", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.more4", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.more5", "chrome://global/locale/intl.properties"); -pref("intl.charsetmenu.browser.unicode", "UTF-8, UTF-16LE, UTF-16BE, UTF-32, UTF-32LE, UTF-32BE"); -pref("intl.charset.detector", "chrome://global/locale/intl.properties"); -pref("intl.charset.default", "chrome://global-platform/locale/intl.properties"); -pref("font.language.group", "chrome://global/locale/intl.properties"); -pref("intl.menuitems.alwaysappendaccesskeys","chrome://global/locale/intl.properties"); -pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/intl.properties"); - // simple gestures support pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate"); pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate"); diff --git a/browser/base/content/browser-doctype.inc b/browser/base/content/browser-doctype.inc index 1b430209cf35..cacd4d3a58c3 100644 --- a/browser/base/content/browser-doctype.inc +++ b/browser/base/content/browser-doctype.inc @@ -5,8 +5,6 @@ %browserDTD; %baseMenuDTD; - -%globalRegionDTD; %charsetDTD; diff --git a/browser/base/content/browser-tabview.js b/browser/base/content/browser-tabview.js index 76a5f9105b8a..a91dae12c1bd 100644 --- a/browser/base/content/browser-tabview.js +++ b/browser/base/content/browser-tabview.js @@ -40,7 +40,7 @@ let TabView = { _window: null, _sessionstore: null, _visibilityID: "tabview-visibility", - + // ---------- get windowTitle() { delete this.windowTitle; @@ -51,7 +51,7 @@ let TabView = { }, // ---------- - init: function TabView_init() { + init: function TabView_init() { // ___ keys this._setBrowserKeyHandlers(); @@ -61,8 +61,21 @@ let TabView = { getService(Ci.nsISessionStore); let data = this._sessionstore.getWindowValue(window, this._visibilityID); - if (data && data == "true") + if (data && data == "true") { this.show(); + } else { + let self = this; + // if a tab is changed from hidden to unhidden and the iframe is not + // initialized, load the iframe and setup the tab. + this._tabShowEventListener = function (event) { + if (!self._window) + self._initFrame(function() { + self._window.UI.onTabSelect(gBrowser.selectedTab); + }); + }; + gBrowser.tabContainer.addEventListener( + "TabShow", this._tabShowEventListener, true); + } }, // ---------- @@ -75,16 +88,16 @@ let TabView = { } else { // ___ find the deck this._deck = document.getElementById("tab-view-deck"); - + // ___ create the frame let iframe = document.createElement("iframe"); iframe.id = "tab-view"; iframe.setAttribute("transparent", "true"); iframe.flex = 1; - + if (typeof callback == "function") iframe.addEventListener("DOMContentLoaded", callback, false); - + iframe.setAttribute("src", "chrome://browser/content/tabview.html"); this._deck.appendChild(iframe); this._window = iframe.contentWindow; @@ -97,11 +110,15 @@ let TabView = { self._sessionstore.setWindowValue(window, self._visibilityID, data); } } - Services.obs.addObserver(observer, "quit-application-requested", false); + + if (this._tabShowEventListener) { + gBrowser.tabContainer.removeEventListener( + "TabShow", this._tabShowEventListener, true); + } } }, - + // ---------- getContentWindow: function TabView_getContentWindow() { return this._window; diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index fefa9baf7db6..59dbb84cebab 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -51,7 +51,7 @@ tabbrowser { -moz-transition: opacity .25s; } -.tabbrowser-tab[pinned] { +.tabbrowser-tabs:not([pinnedonly]) > .tabbrowser-tab[pinned] { position: fixed; display: block; /* position:fixed already does this (bug 579776), but let's be explicit */ } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 955ef500a0f8..5f5fc5825b24 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1378,6 +1378,7 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) { OfflineApps.init(); IndexedDBPromptHelper.init(); gFormSubmitObserver.init(); + AddonManager.addAddonListener(AddonsMgrListener); gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true); @@ -1641,6 +1642,7 @@ function BrowserShutdown() OfflineApps.uninit(); gPrivateBrowsingUI.uninit(); IndexedDBPromptHelper.uninit(); + AddonManager.removeAddonListener(AddonsMgrListener); var enumerator = Services.wm.getEnumerator(null); enumerator.getNext(); @@ -2806,12 +2808,13 @@ function FillInHTMLTooltip(tipElement) #endif // MOZ_SVG var direction = tipElement.ownerDocument.dir; - // If the element is invalid per HTML5 Forms specifications, - // show the constraint validation error message instead of @tooltip. - if (tipElement instanceof HTMLInputElement || - tipElement instanceof HTMLTextAreaElement || - tipElement instanceof HTMLSelectElement || - tipElement instanceof HTMLButtonElement) { + // If the element is invalid per HTML5 Forms specifications and has no title, + // show the constraint validation error message. + if ((tipElement instanceof HTMLInputElement || + tipElement instanceof HTMLTextAreaElement || + tipElement instanceof HTMLSelectElement || + tipElement instanceof HTMLButtonElement) && + !tipElement.hasAttribute('title')) { // If the element is barred from constraint validation or valid, // the validation message will be the empty string. titleText = tipElement.validationMessage; @@ -3431,6 +3434,12 @@ function BrowserCustomizeToolbar() PlacesToolbarHelper.customizeStart(); BookmarksMenuButton.customizeStart(); + let addonBar = document.getElementById("addon-bar"); + if (addonBar.collapsed) { + addonBar.wasCollapsed = addonBar.collapsed; + addonBar.collapsed = false; + } + var customizeURL = "chrome://global/content/customizeToolbar.xul"; gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false); @@ -3489,6 +3498,12 @@ function BrowserToolboxCustomizeDone(aToolboxChanged) { PlacesToolbarHelper.customizeDone(); BookmarksMenuButton.customizeDone(); + let addonBar = document.getElementById("addon-bar"); + if (addonBar.wasCollapsed === true) { + addonBar.collapsed = true; + delete addonBar.wasCollapsed; + } + // The url bar splitter state is dependent on whether stop/reload // and the location bar are combined, so we need this ordering CombinedStopReload.init(); @@ -5167,7 +5182,15 @@ function handleLinkClick(event, href, linkNode) { } function middleMousePaste(event) { - var url = getShortcutOrURI(readFromClipboard()); + let clipboard = readFromClipboard(); + if (!clipboard) + return; + + // Strip embedded newlines and surrounding whitespace, to match the URL + // bar's behavior (stripsurroundingwhitespace) + clipboard.replace(/\s*\n\s*/g, ""); + + let url = getShortcutOrURI(clipboard); try { makeURI(url); } catch (ex) { @@ -6594,10 +6617,15 @@ var FeedHandler = { onFeedButtonClick: function(event) { event.stopPropagation(); - if (event.target.hasAttribute("feed") && - event.eventPhase == Event.AT_TARGET && + let feeds = gBrowser.selectedBrowser.feeds || []; + // If there are multiple feeds, the menu will open, so no need to do + // anything. If there are no feeds, nothing to do either. + if (feeds.length != 1) + return; + + if (event.eventPhase == Event.AT_TARGET && (event.button == 0 || event.button == 1)) { - this.subscribeToFeed(null, event); + this.subscribeToFeed(feeds[0].href, event); } }, @@ -6626,12 +6654,8 @@ var FeedHandler = { while (menuPopup.firstChild) menuPopup.removeChild(menuPopup.firstChild); - if (feeds.length == 1) { - var feedButton = document.getElementById("feed-button"); - if (feedButton) - feedButton.setAttribute("feed", feeds[0].href); + if (feeds.length <= 1) return false; - } // Build the menu showing the available feed choices for viewing. for (var i = 0; i < feeds.length; ++i) { @@ -6704,35 +6728,30 @@ var FeedHandler = { * a page is loaded or the user switches tabs to a page that has feeds. */ updateFeeds: function() { - var feedButton = document.getElementById("feed-button"); + clearTimeout(this._updateFeedTimeout); var feeds = gBrowser.selectedBrowser.feeds; - if (!feeds || feeds.length == 0) { - if (feedButton) { - feedButton.disabled = true; - feedButton.removeAttribute("feed"); - } + var haveFeeds = feeds && feeds.length > 0; + + var feedButton = document.getElementById("feed-button"); + if (feedButton) + feedButton.disabled = !haveFeeds; + + if (!haveFeeds) { this._feedMenuitem.setAttribute("disabled", "true"); - this._feedMenupopup.setAttribute("hidden", "true"); this._feedMenuitem.removeAttribute("hidden"); + this._feedMenupopup.setAttribute("hidden", "true"); + return; + } + + if (feeds.length > 1) { + this._feedMenuitem.setAttribute("hidden", "true"); + this._feedMenupopup.removeAttribute("hidden"); } else { - if (feedButton) - feedButton.disabled = false; - - if (feeds.length > 1) { - this._feedMenuitem.setAttribute("hidden", "true"); - this._feedMenupopup.removeAttribute("hidden"); - if (feedButton) - feedButton.removeAttribute("feed"); - } else { - if (feedButton) - feedButton.setAttribute("feed", feeds[0].href); - - this._feedMenuitem.setAttribute("feed", feeds[0].href); - this._feedMenuitem.removeAttribute("disabled"); - this._feedMenuitem.removeAttribute("hidden"); - this._feedMenupopup.setAttribute("hidden", "true"); - } + this._feedMenuitem.setAttribute("feed", feeds[0].href); + this._feedMenuitem.removeAttribute("disabled"); + this._feedMenuitem.removeAttribute("hidden"); + this._feedMenupopup.setAttribute("hidden", "true"); } }, @@ -6749,10 +6768,13 @@ var FeedHandler = { browserForLink.feeds.push({ href: link.href, title: link.title }); + // If this addition was for the current browser, update the UI. For + // background browsers, we'll update on tab switch. if (browserForLink == gBrowser.selectedBrowser) { - var feedButton = document.getElementById("feed-button"); - if (feedButton) - feedButton.collapsed = false; + // Batch updates to avoid updating the UI for multiple onLinkAdded events + // fired within 100ms of each other. + clearTimeout(this._updateFeedTimeout); + this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100); } } }; @@ -8030,3 +8052,33 @@ function duplicateTabIn(aTab, where, historyIndex) { break; } } + +/* + * When addons are installed/uninstalled, check and see if the number of items + * on the add-on bar changed: + * - If an add-on was installed, incrementing the count, show the bar. + * - If an add-on was uninstalled, and no more items are left, hide the bar. + */ +let AddonsMgrListener = { + get addonBar() document.getElementById("addon-bar"), + get statusBar() document.getElementById("status-bar"), + getAddonBarItemCount: function() { + // Take into account the contents of the status bar shim for the count. + return this.addonBar.childNodes.length - 1 + + this.statusBar.childNodes.length; + }, + onInstalling: function(aAddon) { + this.lastAddonBarCount = this.getAddonBarItemCount(); + }, + onInstalled: function(aAddon) { + if (this.getAddonBarItemCount() > this.lastAddonBarCount) + setToolbarVisibility(this.addonBar, true); + }, + onUninstalling: function(aAddon) { + this.lastAddonBarCount = this.getAddonBarItemCount(); + }, + onUninstalled: function(aAddon) { + if (this.lastAddonBarCount > 0 && this.getAddonBarItemCount() == 0) + setToolbarVisibility(this.addonBar, false); + } +}; diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index ada769680f13..910cbf89c3d4 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -36,6 +36,7 @@ # Rob Campbell # Patrick Walton # David Dahl +# Frank Yan # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or @@ -53,6 +54,7 @@ + @@ -113,9 +115,6 @@ onpopuphidden="if (event.target == this) TabContextMenu.contextTab = null;"> - + - + -