diff --git a/Makefile.in b/Makefile.in index d7898275d015..70fabb451db5 100644 --- a/Makefile.in +++ b/Makefile.in @@ -150,13 +150,6 @@ SYMBOL_INDEX_NAME = \ buildsymbols: ifdef MOZ_CRASHREPORTER -ifdef USE_ELF_HACK - ifeq (mobile,$(MOZ_BUILD_APP)) - $(MAKE) -C mobile/xul/installer elfhack - else - $(MAKE) -C $(MOZ_BUILD_APP)/installer elfhack - endif -endif echo building symbol store $(RM) -r $(DIST)/crashreporter-symbols $(RM) "$(DIST)/$(SYMBOL_ARCHIVE_BASENAME).zip" diff --git a/accessible/public/nsIAccessibleProvider.idl b/accessible/public/nsIAccessibleProvider.idl index a854fba8b0d6..d95054f4b936 100644 --- a/accessible/public/nsIAccessibleProvider.idl +++ b/accessible/public/nsIAccessibleProvider.idl @@ -62,8 +62,8 @@ interface nsIAccessibleProvider : nsISupports const long XULTab = 0x00001017; /** Used for XUL tabs element, a container for tab elements */ const long XULTabs = 0x00001018; - /** Used for XUL deck frame */ - const long XULDeck = 0x00001019; + /** Used for XUL tabpanels element */ + const long XULTabpanels = 0x00001019; const long XULText = 0x0000101A; const long XULTextBox = 0x0000101B; diff --git a/accessible/src/base/AccEvent.cpp b/accessible/src/base/AccEvent.cpp index fe41eb880335..d5ee051153e8 100644 --- a/accessible/src/base/AccEvent.cpp +++ b/accessible/src/base/AccEvent.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -117,10 +118,12 @@ AccReorderEvent::IsShowHideEventTarget(const Accessible* aTarget) const { uint32_t count = mDependentEvents.Length(); for (uint32_t index = count - 1; index < count; index--) { - if (mDependentEvents[index]->mAccessible == aTarget && - mDependentEvents[index]->mEventType == nsIAccessibleEvent::EVENT_SHOW || - mDependentEvents[index]->mEventType == nsIAccessibleEvent::EVENT_HIDE) { - return mDependentEvents[index]->mEventType; + if (mDependentEvents[index]->mAccessible == aTarget) { + uint32_t eventType = mDependentEvents[index]->mEventType; + if (eventType == nsIAccessibleEvent::EVENT_SHOW || + eventType == nsIAccessibleEvent::EVENT_HIDE) { + return mDependentEvents[index]->mEventType; + } } } diff --git a/accessible/src/base/AccTypes.h b/accessible/src/base/AccTypes.h index e707a350ee78..2f5886fbe440 100644 --- a/accessible/src/base/AccTypes.h +++ b/accessible/src/base/AccTypes.h @@ -52,7 +52,7 @@ enum AccType { eMenuPopupType, eProgressType, eRootType, - eXULDeckType, + eXULTabpanelsType, eXULTreeType, eLastAccType = eXULTreeType diff --git a/accessible/src/base/DocManager.cpp b/accessible/src/base/DocManager.cpp index c08248bb8880..f9d4a852741f 100644 --- a/accessible/src/base/DocManager.cpp +++ b/accessible/src/base/DocManager.cpp @@ -49,7 +49,7 @@ DocManager::GetDocAccessible(nsIDocument* aDocument) // Ensure CacheChildren is called before we query cache. ApplicationAcc()->EnsureChildren(); - DocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument); + DocAccessible* docAcc = GetExistingDocAccessible(aDocument); if (docAcc) return docAcc; @@ -181,7 +181,7 @@ DocManager::OnStateChange(nsIWebProgress* aWebProgress, logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags); #endif - DocAccessible* docAcc = mDocAccessibleCache.GetWeak(document); + DocAccessible* docAcc = GetExistingDocAccessible(document); if (!docAcc) return NS_OK; @@ -280,7 +280,7 @@ DocManager::HandleEvent(nsIDOMEvent* aEvent) // We're allowed to not remove listeners when accessible document is // shutdown since we don't keep strong reference on chrome event target and // listeners are removed automatically when chrome event target goes away. - DocAccessible* docAccessible = mDocAccessibleCache.GetWeak(document); + DocAccessible* docAccessible = GetExistingDocAccessible(document); if (docAccessible) docAccessible->Shutdown(); @@ -312,7 +312,7 @@ DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument, { // Document accessible can be created before we were notified the DOM document // was loaded completely. However if it's not created yet then create it. - DocAccessible* docAcc = mDocAccessibleCache.GetWeak(aDocument); + DocAccessible* docAcc = GetExistingDocAccessible(aDocument); if (!docAcc) { docAcc = CreateDocOrRootAccessible(aDocument); if (!docAcc) @@ -358,7 +358,7 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument) // Ignore documents without presshell and not having root frame. nsIPresShell* presShell = aDocument->GetShell(); - if (!presShell || !presShell->GetRootFrame() || presShell->IsDestroying()) + if (!presShell || presShell->IsDestroying()) return nullptr; bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument); diff --git a/accessible/src/base/DocManager.h b/accessible/src/base/DocManager.h index 2cdb5207447d..332a5453d400 100644 --- a/accessible/src/base/DocManager.h +++ b/accessible/src/base/DocManager.h @@ -59,14 +59,6 @@ public: */ Accessible* FindAccessibleInCache(nsINode* aNode) const; - /** - * Return document accessible from the cache. Convenient method for testing. - */ - inline DocAccessible* GetDocAccessibleFromCache(nsIDocument* aDocument) const - { - return mDocAccessibleCache.GetWeak(aDocument); - } - /** * Called by document accessible when it gets shutdown. */ @@ -154,6 +146,18 @@ private: DocAccessibleHashtable mDocAccessibleCache; }; +/** + * Return the existing document accessible for the document if any. + * Note this returns the doc accessible for the primary pres shell if there is + * more than one. + */ +inline DocAccessible* +GetExistingDocAccessible(const nsIDocument* aDocument) +{ + nsIPresShell* ps = aDocument->GetShell(); + return ps ? ps->GetDocAccessible() : nullptr; +} + } // namespace a11y } // namespace mozilla diff --git a/accessible/src/base/Logging.cpp b/accessible/src/base/Logging.cpp index 0268f731c1d7..5cd61a971b76 100644 --- a/accessible/src/base/Logging.cpp +++ b/accessible/src/base/Logging.cpp @@ -400,8 +400,7 @@ logging::DocLoad(const char* aMsg, nsIWebProgress* aWebProgress, } nsCOMPtr documentNode(do_QueryInterface(DOMDocument)); - DocAccessible* document = - GetAccService()->GetDocAccessibleFromCache(documentNode); + DocAccessible* document = GetExistingDocAccessible(documentNode); LogDocInfo(documentNode, document); @@ -425,8 +424,7 @@ logging::DocLoad(const char* aMsg, nsIDocument* aDocumentNode) { MsgBegin(sDocLoadTitle, aMsg); - DocAccessible* document = - GetAccService()->GetDocAccessibleFromCache(aDocumentNode); + DocAccessible* document = GetExistingDocAccessible(aDocumentNode); LogDocInfo(aDocumentNode, document); MsgEnd(); @@ -486,7 +484,7 @@ logging::DocCreate(const char* aMsg, nsIDocument* aDocumentNode, DocAccessible* aDocument) { DocAccessible* document = aDocument ? - aDocument : GetAccService()->GetDocAccessibleFromCache(aDocumentNode); + aDocument : GetExistingDocAccessible(aDocumentNode); MsgBegin(sDocCreateTitle, aMsg); LogDocInfo(aDocumentNode, document); @@ -498,7 +496,7 @@ logging::DocDestroy(const char* aMsg, nsIDocument* aDocumentNode, DocAccessible* aDocument) { DocAccessible* document = aDocument ? - aDocument : GetAccService()->GetDocAccessibleFromCache(aDocumentNode); + aDocument : GetExistingDocAccessible(aDocumentNode); MsgBegin(sDocDestroyTitle, aMsg); LogDocInfo(aDocumentNode, document); diff --git a/accessible/src/base/NotificationController.cpp b/accessible/src/base/NotificationController.cpp index 9e2b8f6b6dd8..68bea8799702 100644 --- a/accessible/src/base/NotificationController.cpp +++ b/accessible/src/base/NotificationController.cpp @@ -9,7 +9,7 @@ #include "nsAccessibilityService.h" #include "nsAccUtils.h" #include "nsCoreUtils.h" -#include "DocAccessible.h" +#include "DocAccessible-inl.h" #include "nsEventShell.h" #include "FocusManager.h" #include "Role.h" @@ -213,6 +213,10 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime) "Pending content insertions while initial accessible tree isn't created!"); } + // Initialize scroll support if needed. + if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized)) + mDocument->AddScrollListener(); + // Process content inserted notifications to update the tree. Process other // notifications like DOM events and then flush event queue. If any new // notifications are queued during this processing then they will be processed @@ -637,7 +641,6 @@ NotificationController::CoalesceTextChangeEventsFor(AccShowEvent* aTailEvent, void NotificationController::CreateTextChangeEventFor(AccMutationEvent* aEvent) { - DocAccessible* document = aEvent->GetDocAccessible(); Accessible* container = aEvent->mAccessible->Parent(); if (!container) return; diff --git a/accessible/src/base/nsARIAMap.cpp b/accessible/src/base/nsARIAMap.cpp index c718b122d165..4624a6eb2d40 100644 --- a/accessible/src/base/nsARIAMap.cpp +++ b/accessible/src/base/nsARIAMap.cpp @@ -249,7 +249,7 @@ static nsRoleMapEntry sWAIRoleMaps[] = eNoValue, eNoAction, eNoLiveAttr, - eSelect, + eListControl | eSelect, kNoReqStates, eARIAMultiSelectable, eARIAReadonly diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index 1618dd0b42b8..e99c24e7382f 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -60,6 +60,7 @@ #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/Util.h" +#include "nsDeckFrame.h" #ifdef MOZ_XUL #include "XULAlertAccessible.h" @@ -185,6 +186,7 @@ Accessible* nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell, bool aCanCreate) { + nsIPresShell* ps = aPresShell; nsIDocument* documentNode = aPresShell->GetDocument(); if (documentNode) { nsCOMPtr container = documentNode->GetContainer(); @@ -196,11 +198,10 @@ nsAccessibilityService::GetRootDocumentAccessible(nsIPresShell* aPresShell, nsCOMPtr docShell(do_QueryInterface(rootTreeItem)); nsCOMPtr presShell; docShell->GetPresShell(getter_AddRefs(presShell)); - documentNode = presShell->GetDocument(); + ps = presShell; } - return aCanCreate ? - GetDocAccessible(documentNode) : GetDocAccessibleFromCache(documentNode); + return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible(); } } return nullptr; @@ -254,6 +255,47 @@ nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame, return nullptr; } +void +nsAccessibilityService::DeckPanelSwitched(nsIPresShell* aPresShell, + nsIContent* aDeckNode, + nsIFrame* aPrevBoxFrame, + nsIFrame* aCurrentBoxFrame) +{ + // Ignore tabpanels elements (a deck having an accessible) since their + // children are accessible not depending on selected tab. + DocAccessible* document = GetDocAccessible(aPresShell); + if (!document || document->HasAccessible(aDeckNode)) + return; + + if (aPrevBoxFrame) { + nsIContent* panelNode = aPrevBoxFrame->GetContent(); +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eTree)) { + logging::MsgBegin("TREE", "deck panel unselected"); + logging::Node("container", panelNode); + logging::Node("content", aDeckNode); + logging::MsgEnd(); + } +#endif + + document->ContentRemoved(aDeckNode, panelNode); + } + + if (aCurrentBoxFrame) { + nsIContent* panelNode = aCurrentBoxFrame->GetContent(); +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eTree)) { + logging::MsgBegin("TREE", "deck panel selected"); + logging::Node("container", panelNode); + logging::Node("content", aDeckNode); + logging::MsgEnd(); + } +#endif + + document->ContentInserted(aDeckNode, panelNode, panelNode->GetNextSibling()); + } +} + void nsAccessibilityService::ContentRangeInserted(nsIPresShell* aPresShell, nsIContent* aContainer, @@ -364,15 +406,12 @@ nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) void nsAccessibilityService::PresShellActivated(nsIPresShell* aPresShell) { - nsIDocument* DOMDoc = aPresShell->GetDocument(); - if (DOMDoc) { - DocAccessible* document = GetDocAccessibleFromCache(DOMDoc); - if (document) { - RootAccessible* rootDocument = document->RootAccessible(); - NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!"); - if (rootDocument) - rootDocument->DocumentActivated(document); - } + DocAccessible* document = aPresShell->GetDocAccessible(); + if (document) { + RootAccessible* rootDocument = document->RootAccessible(); + NS_ASSERTION(rootDocument, "Entirely broken tree: no root document!"); + if (rootDocument) + rootDocument->DocumentActivated(document); } } @@ -608,7 +647,7 @@ nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode, if (!accessible) { nsCOMPtr document(do_QueryInterface(node)); if (document) - accessible = GetDocAccessibleFromCache(document); + accessible = GetExistingDocAccessible(document); } NS_IF_ADDREF(*aAccessible = accessible); @@ -804,7 +843,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, newAcc = new ARIAGridCellAccessibleWrap(content, document); } - } else if ((roleMapEntry->accTypes & eTable) && + } else if ((roleMapEntry->IsOfType(eTable)) && frame->AccessibleType() != eHTMLTableType) { newAcc = new ARIAGridAccessibleWrap(content, document); } @@ -824,7 +863,7 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, if (!roleMapEntry && newAcc) { if (frame->AccessibleType() == eHTMLTableRowType) { nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap(); - if (contextRoleMap && !(contextRoleMap->accTypes & eTable)) + if (contextRoleMap && !(contextRoleMap->IsOfType(eTable))) roleMapEntry = &nsARIAMap::gEmptyRoleMap; } else if (frame->AccessibleType() == eHTMLTableCellType && @@ -836,35 +875,50 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode, content->Tag() == nsGkAtoms::dd || frame->AccessibleType() == eHTMLLiType) { nsRoleMapEntry* contextRoleMap = aContext->ARIARoleMap(); - if (contextRoleMap && !(contextRoleMap->accTypes & eList)) + if (contextRoleMap && !(contextRoleMap->IsOfType(eList))) roleMapEntry = &nsARIAMap::gEmptyRoleMap; } } } } - if (!newAcc) { + // Accessible XBL types and deck stuff are used in XUL only currently. + if (!newAcc && content->IsXUL()) { + // No accessible for not selected deck panel and its children. + if (!aContext->IsXULTabpanels()) { + nsDeckFrame* deckFrame = do_QueryFrame(frame->GetParent()); + if (deckFrame && deckFrame->GetSelectedBox() != frame) { + if (aIsSubtreeHidden) + *aIsSubtreeHidden = true; + + return nullptr; + } + } + // Elements may implement nsIAccessibleProvider via XBL. This allows them to // say what kind of accessible to create. newAcc = CreateAccessibleByType(content, document); + + // Any XUL box can be used as tabpanel, make sure we create a proper + // accessible for it. + if (!newAcc && aContext->IsXULTabpanels() && + content->GetParent() == aContext->GetContent()) { + nsIAtom* frameType = frame->GetType(); + if (frameType == nsGkAtoms::boxFrame || + frameType == nsGkAtoms::scrollFrame) { + newAcc = new XULTabpanelAccessible(content, document); + } + } } if (!newAcc) { - // xul:deck does not have XBL and nsIFrame::CreateAccessible() is only called - // on HTML elements - nsIAtom* tag = content->Tag(); - if ((tag == nsGkAtoms::deck) || (tag == nsGkAtoms::tabpanels)) { - newAcc = new XULDeckAccessible(content, document); - } else if (content->IsSVG(nsGkAtoms::svg)) { + if (content->IsSVG(nsGkAtoms::svg)) { newAcc = new EnumRoleAccessible(content, document, roles::DIAGRAM); } else if (content->IsMathML(nsGkAtoms::math)) { newAcc = new EnumRoleAccessible(content, document, roles::EQUATION); } } - if (!newAcc) - newAcc = CreateAccessibleForDeckChild(frame, content, document); - // If no accessible, see if we need to create a generic accessible because // of some property that makes this object interesting // We don't do this for , , , etc. which @@ -1011,8 +1065,8 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent, accessible = new XULComboboxAccessible(aContent, aDoc); break; - case nsIAccessibleProvider::XULDeck: - accessible = new XULDeckAccessible(aContent, aDoc); + case nsIAccessibleProvider::XULTabpanels: + accessible = new XULTabpanelsAccessible(aContent, aDoc); break; case nsIAccessibleProvider::XULDropmarker: @@ -1406,6 +1460,9 @@ nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame, case eTextLeafType: newAcc = new TextLeafAccessibleWrap(aContent, document); break; + default: + MOZ_ASSERT(false); + break; } return newAcc.forget(); @@ -1480,37 +1537,6 @@ NS_GetAccessibilityService(nsIAccessibilityService** aResult) //////////////////////////////////////////////////////////////////////////////// // nsAccessibilityService private (DON'T put methods here) -already_AddRefed -nsAccessibilityService::CreateAccessibleForDeckChild(nsIFrame* aFrame, - nsIContent* aContent, - DocAccessible* aDoc) -{ - if (aFrame->GetType() == nsGkAtoms::boxFrame || - aFrame->GetType() == nsGkAtoms::scrollFrame) { - - nsIFrame* parentFrame = aFrame->GetParent(); - if (parentFrame && parentFrame->GetType() == nsGkAtoms::deckFrame) { - // If deck frame is for xul:tabpanels element then the given node has - // tabpanel accessible. - nsIContent* parentContent = parentFrame->GetContent(); -#ifdef MOZ_XUL - if (parentContent->NodeInfo()->Equals(nsGkAtoms::tabpanels, - kNameSpaceID_XUL)) { - Accessible* accessible = new XULTabpanelAccessible(aContent, aDoc); - NS_ADDREF(accessible); - return accessible; - } -#endif - Accessible* accessible = new EnumRoleAccessible(aContent, aDoc, - roles::PROPERTYPAGE); - NS_ADDREF(accessible); - return accessible; - } - } - - return nullptr; -} - #ifdef MOZ_XUL already_AddRefed nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent, diff --git a/accessible/src/base/nsAccessibilityService.h b/accessible/src/base/nsAccessibilityService.h index d30bca016c1a..dbdf66f53d1d 100644 --- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -64,6 +64,13 @@ public: virtual Accessible* AddNativeRootAccessible(void* aAtkAccessible); virtual void RemoveNativeRootAccessible(Accessible* aRootAccessible); + /** + * Notification used to update the accessible tree when deck panel is + * switched. + */ + void DeckPanelSwitched(nsIPresShell* aPresShell, nsIContent* aDeckNode, + nsIFrame* aPrevBoxFrame, nsIFrame* aCurrentBoxFrame); + /** * Notification used to update the accessible tree when new content is * inserted. @@ -173,13 +180,6 @@ private: CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent, Accessible* aContext); - /** - * Create accessible if parent is a deck frame. - */ - already_AddRefed - CreateAccessibleForDeckChild(nsIFrame* aFrame, nsIContent* aContent, - DocAccessible* aDoc); - #ifdef MOZ_XUL /** * Create accessible for XUL tree element. diff --git a/accessible/src/generic/Accessible.cpp b/accessible/src/generic/Accessible.cpp index ab149f68321c..1e2ccb86222c 100644 --- a/accessible/src/generic/Accessible.cpp +++ b/accessible/src/generic/Accessible.cpp @@ -264,6 +264,16 @@ Accessible::Name(nsString& aName) aName.CompressWhitespace(); return eNameFromTooltip; } + } else if (mContent->IsSVG()) { + // If user agents need to choose among multiple ‘desc’ or ‘title’ elements + // for processing, the user agent shall choose the first one. + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (childElm->IsSVG(nsGkAtoms::title)) { + nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName); + return eNameFromTooltip; + } + } } if (nameFlag != eNoNameOnPurpose) @@ -307,25 +317,40 @@ Accessible::Description(nsString& aDescription) // Try XUL description text XULDescriptionIterator iter(Document(), mContent); Accessible* descr = nullptr; - while ((descr = iter.Next())) + while ((descr = iter.Next())) { nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(), &aDescription); } + } - if (aDescription.IsEmpty()) { - nsIAtom *descAtom = isXUL ? nsGkAtoms::tooltiptext : - nsGkAtoms::title; - if (mContent->GetAttr(kNameSpaceID_None, descAtom, aDescription)) { - nsAutoString name; - Name(name); - if (name.IsEmpty() || aDescription == name) - // Don't use tooltip for a description if this object - // has no name or the tooltip is the same as the name - aDescription.Truncate(); + if (aDescription.IsEmpty()) { + // Keep the Name() method logic. + if (mContent->IsHTML()) { + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription); + } else if (mContent->IsXUL()) { + mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription); + } else if (mContent->IsSVG()) { + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (childElm->IsSVG(nsGkAtoms::title)) { + nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, + &aDescription); + break; + } } } + + if (!aDescription.IsEmpty()) { + nsAutoString name; + ENameValueFlag nameFlag = Name(name); + + // Don't use tooltip for a description if it was used for a name. + if (nameFlag == eNameFromTooltip) + aDescription.Truncate(); + } } - aDescription.CompressWhitespace(); + } + aDescription.CompressWhitespace(); } NS_IMETHODIMP @@ -612,6 +637,7 @@ Accessible::VisibilityState() deckFrame->GetContent()->Tag() == nsGkAtoms::tabpanels) return states::OFFSCREEN; + NS_NOTREACHED("Children of not selected deck panel are not accessible."); return states::INVISIBLE; } @@ -1589,17 +1615,44 @@ Accessible::GetValue(nsAString& aValue) void Accessible::Value(nsString& aValue) { - if (mRoleMapEntry) { - if (mRoleMapEntry->valueRule == eNoValue) - return; + if (!mRoleMapEntry) + return; - // aria-valuenow is a number, and aria-valuetext is the optional text equivalent - // For the string value, we will try the optional text equivalent first + if (mRoleMapEntry->valueRule != eNoValue) { + // aria-valuenow is a number, and aria-valuetext is the optional text + // equivalent. For the string value, we will try the optional text + // equivalent first. if (!mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext, aValue)) { mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, aValue); } + return; + } + + // Value of combobox is a text of current or selected item. + if (mRoleMapEntry->Is(nsGkAtoms::combobox)) { + Accessible* option = CurrentItem(); + if (!option) { + Accessible* listbox = nullptr; + IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns); + while ((listbox = iter.Next()) && !listbox->IsListControl()); + + if (!listbox) { + uint32_t childCount = ChildCount(); + for (uint32_t idx = 0; idx < childCount; idx++) { + Accessible* child = mChildren.ElementAt(idx); + if (child->IsListControl()) + listbox = child; + } + } + + if (listbox) + option = listbox->GetSelectedItem(0); + } + + if (option) + nsTextEquivUtils::GetNameFromSubtree(option, aValue); } } @@ -2466,6 +2519,18 @@ Accessible::NativeName(nsString& aName) if (mContent->IsXUL()) return GetXULName(aName); + if (mContent->IsSVG()) { + // If user agents need to choose among multiple ‘desc’ or ‘title’ elements + // for processing, the user agent shall choose the first one. + for (nsIContent* childElm = mContent->GetFirstChild(); childElm; + childElm = childElm->GetNextSibling()) { + if (childElm->IsSVG(nsGkAtoms::desc)) { + nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName); + return eNameOK; + } + } + } + return eNameOK; } diff --git a/accessible/src/generic/Accessible.h b/accessible/src/generic/Accessible.h index 9eb2e62ab4f8..007b345bb6ee 100644 --- a/accessible/src/generic/Accessible.h +++ b/accessible/src/generic/Accessible.h @@ -520,7 +520,7 @@ public: bool IsTextLeaf() const { return mType == eTextLeafType; } TextLeafAccessible* AsTextLeaf(); - bool IsXULDeck() const { return mType == eXULDeckType; } + bool IsXULTabpanels() const { return mType == eXULTabpanelsType; } bool IsXULTree() const { return mType == eXULTreeType; } XULTreeAccessible* AsXULTree(); diff --git a/accessible/src/generic/DocAccessible-inl.h b/accessible/src/generic/DocAccessible-inl.h index 6ae0a371ac03..dbeeca495048 100644 --- a/accessible/src/generic/DocAccessible-inl.h +++ b/accessible/src/generic/DocAccessible-inl.h @@ -65,6 +65,33 @@ DocAccessible::UpdateText(nsIContent* aTextNode) mNotificationController->ScheduleTextUpdate(aTextNode); } +inline void +DocAccessible::AddScrollListener() +{ + // Delay scroll initializing until the document has a root frame. + if (!mPresShell->GetRootFrame()) + return; + + mDocFlags |= eScrollInitialized; + nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable(); + if (sf) { + sf->AddScrollPositionListener(this); + +#ifdef A11Y_LOG + if (logging::IsEnabled(logging::eDocCreate)) + logging::Text("add scroll listener"); +#endif + } +} + +inline void +DocAccessible::RemoveScrollListener() +{ + nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollable(); + if (sf) + sf->RemoveScrollPositionListener(this); +} + inline void DocAccessible::NotifyOfLoad(uint32_t aLoadEventType) { diff --git a/accessible/src/generic/DocAccessible.cpp b/accessible/src/generic/DocAccessible.cpp index 03ce46e45001..2436d7275110 100644 --- a/accessible/src/generic/DocAccessible.cpp +++ b/accessible/src/generic/DocAccessible.cpp @@ -75,7 +75,7 @@ DocAccessible:: nsIPresShell* aPresShell) : HyperTextAccessibleWrap(aRootContent, this), mDocumentNode(aDocument), mScrollPositionChangedTicks(0), - mLoadState(eTreeConstructionPending), mLoadEventType(0), + mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0), mVirtualCursor(nullptr), mPresShell(aPresShell) { @@ -93,18 +93,6 @@ DocAccessible:: // If this is a XUL Document, it should not implement nsHyperText if (mDocumentNode && mDocumentNode->IsXUL()) mGenericTypes &= ~eHyperText; - - // For GTK+ native window, we do nothing here. - if (!mDocumentNode) - return; - - // DocManager creates document accessible when scrollable frame is - // available already, it should be safe time to add scroll listener. - AddScrollListener(); - - // We provide a virtual cursor if this is a root doc or if it's a tab doc. - mIsCursorable = (!(mDocumentNode->GetParentDocument()) || - nsCoreUtils::IsTabDocument(mDocumentNode)); } DocAccessible::~DocAccessible() @@ -145,7 +133,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DocAccessible) NS_INTERFACE_MAP_ENTRY(nsIAccessiblePivotObserver) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIAccessibleDocument) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIAccessibleCursorable, - mIsCursorable) + (mDocFlags & eCursorable)) foundInterface = 0; nsresult status; @@ -502,7 +490,8 @@ DocAccessible::GetVirtualCursor(nsIAccessiblePivot** aVirtualCursor) if (IsDefunct()) return NS_ERROR_FAILURE; - NS_ENSURE_TRUE(mIsCursorable, NS_ERROR_NOT_IMPLEMENTED); + if (!(mDocFlags & eCursorable)) + return NS_OK; if (!mVirtualCursor) { mVirtualCursor = new nsAccessiblePivot(this); @@ -603,8 +592,6 @@ DocAccessible::Shutdown() logging::DocDestroy("document shutdown", mDocumentNode, this); #endif - mPresShell->SetDocAccessible(nullptr); - if (mNotificationController) { mNotificationController->Shutdown(); mNotificationController = nullptr; @@ -640,6 +627,7 @@ DocAccessible::Shutdown() mVirtualCursor = nullptr; } + mPresShell->SetDocAccessible(nullptr); mPresShell = nullptr; // Avoid reentrancy mDependentIDsHash.Clear(); @@ -798,36 +786,6 @@ DocAccessible::ScrollTimerCallback(nsITimer* aTimer, void* aClosure) } } -// DocAccessible protected member -void -DocAccessible::AddScrollListener() -{ - if (!mPresShell) - return; - - nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal(); - if (sf) { - sf->AddScrollPositionListener(this); -#ifdef A11Y_LOG - if (logging::IsEnabled(logging::eDocCreate)) - logging::Text("add scroll listener"); -#endif - } -} - -// DocAccessible protected member -void -DocAccessible::RemoveScrollListener() -{ - if (!mPresShell) - return; - - nsIScrollableFrame* sf = mPresShell->GetRootScrollFrameAsScrollableExternal(); - if (sf) { - sf->RemoveScrollPositionListener(this); - } -} - //////////////////////////////////////////////////////////////////////////////// // nsIScrollPositionListener @@ -1511,14 +1469,23 @@ DocAccessible::NotifyOfLoading(bool aIsReloading) void DocAccessible::DoInitialUpdate() { + if (nsCoreUtils::IsTabDocument(mDocumentNode)) + mDocFlags |= eTabDocument; + + // We provide a virtual cursor if this is a root doc or if it's a tab doc. + if (!mDocumentNode->GetParentDocument() || (mDocFlags & eTabDocument)) + mDocFlags |= eCursorable; + mLoadState |= eTreeConstructed; // The content element may be changed before the initial update and then we // miss the notification (since content tree change notifications are ignored // prior to initial update). Make sure the content element is valid. nsIContent* contentElm = nsCoreUtils::GetRoleContent(mDocumentNode); - if (mContent != contentElm) + if (mContent != contentElm) { mContent = contentElm; + SetRoleMapEntry(aria::GetRoleMap(mContent)); + } // Build initial tree. CacheChildrenInSubtree(this); @@ -1739,8 +1706,10 @@ DocAccessible::ProcessContentInserted(Accessible* aContainer, if (aContainer == this) { // If new root content has been inserted then update it. nsIContent* rootContent = nsCoreUtils::GetRoleContent(mDocumentNode); - if (rootContent != mContent) + if (rootContent != mContent) { mContent = rootContent; + SetRoleMapEntry(aria::GetRoleMap(mContent)); + } // Continue to update the tree even if we don't have root content. // For example, elements may be inserted under the document element while diff --git a/accessible/src/generic/DocAccessible.h b/accessible/src/generic/DocAccessible.h index 22c162ef85ee..13117076b744 100644 --- a/accessible/src/generic/DocAccessible.h +++ b/accessible/src/generic/DocAccessible.h @@ -333,8 +333,11 @@ protected: */ void ProcessLoad(); - void AddScrollListener(); - void RemoveScrollListener(); + /** + * Add/remove scroll listeners, @see nsIScrollPositionListener interface. + */ + void AddScrollListener(); + void RemoveScrollListener(); /** * Append the given document accessible to this document's child document @@ -482,6 +485,20 @@ protected: protected: + /** + * State and property flags, kept by mDocFlags. + */ + enum { + // Whether scroll listeners were added. + eScrollInitialized = 1 << 0, + + // Whether we support nsIAccessibleCursorable. + eCursorable = 1 << 1, + + // Whether the document is a tab document. + eTabDocument = 1 << 2 + }; + /** * Cache of accessibles within this document accessible. */ @@ -496,7 +513,12 @@ protected: /** * Bit mask of document load states (@see LoadState). */ - uint32_t mLoadState; + uint32_t mLoadState : 3; + + /** + * Bit mask of other states and props. + */ + uint32_t mDocFlags : 28; /** * Type of document load event fired after the document is loaded completely. @@ -516,11 +538,6 @@ protected: nsTArray > mChildDocuments; - /** - * Whether we support nsIAccessibleCursorable, used when querying the interface. - */ - bool mIsCursorable; - /** * The virtual cursor of the document when it supports nsIAccessibleCursorable. */ diff --git a/accessible/src/html/HTMLFormControlAccessible.cpp b/accessible/src/html/HTMLFormControlAccessible.cpp index d2462a2b56ca..976409265360 100644 --- a/accessible/src/html/HTMLFormControlAccessible.cpp +++ b/accessible/src/html/HTMLFormControlAccessible.cpp @@ -358,7 +358,7 @@ HTMLTextFieldAccessible::Value(nsString& aValue) textArea->GetValue(aValue); return; } - + nsHTMLInputElement* input = nsHTMLInputElement::FromContent(mContent); if (input) input->GetValue(aValue); diff --git a/accessible/src/html/HTMLTableAccessible.cpp b/accessible/src/html/HTMLTableAccessible.cpp index 81b17825d229..4d6a2b1de1cb 100644 --- a/accessible/src/html/HTMLTableAccessible.cpp +++ b/accessible/src/html/HTMLTableAccessible.cpp @@ -502,7 +502,8 @@ HTMLTableAccessible::SelectedCellCount() int32_t startRow = -1, startCol = -1; cellFrame->GetRowIndex(startRow); cellFrame->GetColIndex(startCol); - if (startRow == rowIdx && startCol == colIdx) + if (startRow >= 0 && (uint32_t)startRow == rowIdx && + startCol >= 0 && (uint32_t)startCol == colIdx) count++; } } @@ -551,7 +552,8 @@ HTMLTableAccessible::SelectedCells(nsTArray* aCells) int32_t startCol = -1, startRow = -1; cellFrame->GetRowIndex(startRow); cellFrame->GetColIndex(startCol); - if (startRow != rowIdx || startCol != colIdx) + if ((startRow >= 0 && (uint32_t)startRow != rowIdx) || + (startCol >= 0 && (uint32_t)startCol != colIdx)) continue; Accessible* cell = mDoc->GetAccessible(cellFrame->GetContent()); @@ -577,7 +579,8 @@ HTMLTableAccessible::SelectedCellIndices(nsTArray* aCells) int32_t startRow = -1, startCol = -1; cellFrame->GetColIndex(startCol); cellFrame->GetRowIndex(startRow); - if (startRow == rowIdx && startCol == colIdx) + if (startRow >= 0 && (uint32_t)startRow == rowIdx && + startCol >= 0 && (uint32_t)startCol == colIdx) aCells->AppendElement(CellIndexAt(rowIdx, colIdx)); } } diff --git a/accessible/src/jsat/EventManager.jsm b/accessible/src/jsat/EventManager.jsm index 76b22861daed..c1f6e697c860 100644 --- a/accessible/src/jsat/EventManager.jsm +++ b/accessible/src/jsat/EventManager.jsm @@ -191,7 +191,7 @@ this.EventManager = { if (txtIface.characterCount) throw x; } - this.present(Presentation, textChanged( + this.present(Presentation.textChanged( isInserted, event.start, event.length, text, event.modifiedText)); } diff --git a/accessible/src/mac/AccessibleWrap.mm b/accessible/src/mac/AccessibleWrap.mm index b02c8d7bac8b..87b53380ae8c 100644 --- a/accessible/src/mac/AccessibleWrap.mm +++ b/accessible/src/mac/AccessibleWrap.mm @@ -60,9 +60,9 @@ AccessibleWrap::GetNativeType () { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; - if (IsXULDeck()) + if (IsXULTabpanels()) return [mozPaneAccessible class]; - + roles::Role role = Role(); switch (role) { case roles::PUSHBUTTON: diff --git a/accessible/src/msaa/DocAccessibleWrap.cpp b/accessible/src/msaa/DocAccessibleWrap.cpp index 49010a4ce197..8fd23d488887 100644 --- a/accessible/src/msaa/DocAccessibleWrap.cpp +++ b/accessible/src/msaa/DocAccessibleWrap.cpp @@ -223,7 +223,7 @@ DocAccessibleWrap::Shutdown() // Do window emulation specific shutdown if emulation was started. if (nsWinUtils::IsWindowEmulationStarted()) { // Destroy window created for root document. - if (nsCoreUtils::IsTabDocument(mDocumentNode)) { + if (mDocFlags & eTabDocument) { sHWNDCache.Remove(mHWND); ::DestroyWindow(static_cast(mHWND)); } @@ -253,7 +253,7 @@ DocAccessibleWrap::DoInitialUpdate() if (nsWinUtils::IsWindowEmulationStarted()) { // Create window for tab document. - if (nsCoreUtils::IsTabDocument(mDocumentNode)) { + if (mDocFlags & eTabDocument) { mozilla::dom::TabChild* tabChild = mozilla::dom::GetTabChildFrom(mDocumentNode->GetShell()); diff --git a/accessible/src/windows/sdn/sdnAccessible-inl.h b/accessible/src/windows/sdn/sdnAccessible-inl.h index a2401a81cdda..40af20ee63a2 100644 --- a/accessible/src/windows/sdn/sdnAccessible-inl.h +++ b/accessible/src/windows/sdn/sdnAccessible-inl.h @@ -18,9 +18,7 @@ namespace a11y { inline DocAccessible* sdnAccessible::GetDocument() const { - DocManager* docMgr = GetAccService(); - return docMgr ? - docMgr->GetDocAccessibleFromCache(mNode->OwnerDoc()) : nullptr; + return GetExistingDocAccessible(mNode->OwnerDoc()); } inline Accessible* diff --git a/accessible/src/xul/XULTabAccessible.cpp b/accessible/src/xul/XULTabAccessible.cpp index e46a447ed8a3..7f15827de3fc 100644 --- a/accessible/src/xul/XULTabAccessible.cpp +++ b/accessible/src/xul/XULTabAccessible.cpp @@ -163,11 +163,11 @@ XULTabsAccessible::NativeName(nsString& aName) //////////////////////////////////////////////////////////////////////////////// -// XULDeckAccessible +// XULTabpanelsAccessible //////////////////////////////////////////////////////////////////////////////// role -XULDeckAccessible::NativeRole() +XULTabpanelsAccessible::NativeRole() { return roles::PANE; } diff --git a/accessible/src/xul/XULTabAccessible.h b/accessible/src/xul/XULTabAccessible.h index 260035bf7974..b8fe6ecee099 100644 --- a/accessible/src/xul/XULTabAccessible.h +++ b/accessible/src/xul/XULTabAccessible.h @@ -62,12 +62,12 @@ protected: /** * A container of tab panels, xul:tabpanels element. */ -class XULDeckAccessible : public AccessibleWrap +class XULTabpanelsAccessible : public AccessibleWrap { public: - XULDeckAccessible(nsIContent* aContent, DocAccessible* aDoc) : + XULTabpanelsAccessible(nsIContent* aContent, DocAccessible* aDoc) : AccessibleWrap(aContent, aDoc) - { mType = eXULDeckType; } + { mType = eXULTabpanelsType; } // Accessible virtual a11y::role NativeRole(); diff --git a/accessible/tests/mochitest/name.js b/accessible/tests/mochitest/name.js index 8bd0f4aa1dd2..c8a940c3bb33 100644 --- a/accessible/tests/mochitest/name.js +++ b/accessible/tests/mochitest/name.js @@ -17,3 +17,16 @@ function testName(aAccOrElmOrID, aName, aMsg) } return acc; } + +/** + * Test accessible description for the given accessible. + */ +function testDescr(aAccOrElmOrID, aDescr) +{ + var acc = getAccessible(aAccOrElmOrID); + if (!acc) + return; + + is(acc.description, aDescr, + "Wrong description for " + prettyName(aAccOrElmOrID)); +} diff --git a/accessible/tests/mochitest/name/Makefile.in b/accessible/tests/mochitest/name/Makefile.in index 79d4e869b0ee..4739731aaf5e 100644 --- a/accessible/tests/mochitest/name/Makefile.in +++ b/accessible/tests/mochitest/name/Makefile.in @@ -21,6 +21,7 @@ MOCHITEST_A11Y_FILES =\ test_link.html \ test_list.html \ test_markup.html \ + test_svg.html \ test_browserui.xul \ test_tree.xul \ markuprules.xml \ diff --git a/accessible/tests/mochitest/name/test_svg.html b/accessible/tests/mochitest/name/test_svg.html new file mode 100644 index 000000000000..2a8e7b7505c4 --- /dev/null +++ b/accessible/tests/mochitest/name/test_svg.html @@ -0,0 +1,56 @@ + + + + Accessible name and description for SVG elements + + + + + + + + + + + + + + + Mozilla Bug 459357 + +

+ +
+  
+ + + A description + A name + + + + A tooltip + + + + diff --git a/accessible/tests/mochitest/states/test_frames.html b/accessible/tests/mochitest/states/test_frames.html index 823ce278e30a..bc5de6a6db2b 100644 --- a/accessible/tests/mochitest/states/test_frames.html +++ b/accessible/tests/mochitest/states/test_frames.html @@ -32,7 +32,6 @@ "test1: frameDocCheckbox"); testStates(frameDocTextbox, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0, "test1: frameDocTextbox"); - frameDoc.designMode = "on"; testStates(frameDoc, 0, EXT_STATE_EDITABLE, STATE_READONLY, 0, "test2: frameDoc"); diff --git a/accessible/tests/mochitest/states/test_visibility.xul b/accessible/tests/mochitest/states/test_visibility.xul index 5978ddb9b7f5..3f9991131721 100644 --- a/accessible/tests/mochitest/states/test_visibility.xul +++ b/accessible/tests/mochitest/states/test_visibility.xul @@ -20,7 +20,6 @@ + + + + + + Mozilla Bug 814836 + + +

+ +
+      
+ + + + + + This is the first page + + + +
@@ -134,10 +140,11 @@ fh.addEntry("field7", "value");
 fh.addEntry("field8", "value");
 fh.addEntry("field9", "value");
 fh.addEntry("field10", "42");
+fh.addEntry("field11", "2010-10-10");
 fh.addEntry("searchbar-history", "blacklist test");
 
 // All these non-implemeted types might need autocomplete tests in the future.
-var todoTypes = [ "datetime", "date", "month", "week", "time", "datetime-local",
+var todoTypes = [ "datetime", "month", "week", "time", "datetime-local",
                   "range", "color" ];
 var todoInput = document.createElement("input");
 for (var type of todoTypes) {
@@ -730,6 +737,17 @@ function runTest(testNum) {
         doKey("return");
         checkForm("42");
 
+        input = $_(14, "field11");
+        restoreForm();
+        doKey("down");
+        break;
+
+    case 405:
+        checkMenuEntries(["2010-10-10"]);
+        doKey("down");
+        doKey("return");
+        checkForm("2010-10-10");
+
         // Go to test 500.
         fh.addEntry("field1", "value1");
         input = $_(1, "field1");
diff --git a/toolkit/components/social/MozSocialAPI.jsm b/toolkit/components/social/MozSocialAPI.jsm
index 34324a29f2c7..c4a9c8df39ca 100644
--- a/toolkit/components/social/MozSocialAPI.jsm
+++ b/toolkit/components/social/MozSocialAPI.jsm
@@ -70,12 +70,11 @@ function injectController(doc, topic, data) {
 
 // Loads mozSocial support functions associated with provider into targetWindow
 function attachToWindow(provider, targetWindow) {
-  // If the loaded document isn't from the provider's origin, don't attach
-  // the mozSocial API.
-  let origin = provider.origin;
+  // If the loaded document isn't from the provider's origin (or a protocol
+  // that inherits the principal), don't attach the mozSocial API.
   let targetDocURI = targetWindow.document.documentURIObject;
-  if (provider.origin != targetDocURI.prePath) {
-    let msg = "MozSocialAPI: not attaching mozSocial API for " + origin +
+  if (!provider.isSameOrigin(targetDocURI)) {
+    let msg = "MozSocialAPI: not attaching mozSocial API for " + provider.origin +
               " to " + targetDocURI.spec + " since origins differ."
     Services.console.logStringMessage(msg);
     return;
@@ -125,10 +124,9 @@ function attachToWindow(provider, targetWindow) {
         if (!chromeWindow.SocialFlyout)
           return;
         let url = targetWindow.document.documentURIObject.resolve(toURL);
-        let fullURL = ensureProviderOrigin(provider, url);
-        if (!fullURL)
+        if (!provider.isSameOrigin(url))
           return;
-        chromeWindow.SocialFlyout.open(fullURL, offset, callback);
+        chromeWindow.SocialFlyout.open(url, offset, callback);
       }
     },
     closePanel: {
@@ -229,26 +227,6 @@ function getChromeWindow(contentWin) {
                    .getInterface(Ci.nsIDOMWindow);
 }
 
-function ensureProviderOrigin(provider, url) {
-  // resolve partial URLs and check prePath matches
-  let uri;
-  let fullURL;
-  try {
-    fullURL = Services.io.newURI(provider.origin, null, null).resolve(url);
-    uri = Services.io.newURI(fullURL, null, null);
-  } catch (ex) {
-    Cu.reportError("mozSocial: failed to resolve window URL: " + url + "; " + ex);
-    return null;
-  }
-
-  if (provider.origin != uri.prePath) {
-    Cu.reportError("mozSocial: unable to load new location, " +
-                   provider.origin + " != " + uri.prePath);
-    return null;
-  }
-  return fullURL;
-}
-
 function isWindowGoodForChats(win) {
   return win.SocialChatBar && win.SocialChatBar.isAvailable;
 }
@@ -289,10 +267,10 @@ this.openChatWindow =
   chromeWindow = findChromeWindowForChats(chromeWindow);
   if (!chromeWindow)
     return;
-  let fullURL = ensureProviderOrigin(provider, url);
-  if (!fullURL)
+  let fullURI = provider.resolveUri(url);
+  if (!provider.isSameOrigin(fullURI))
     return;
-  chromeWindow.SocialChatBar.openChat(provider, fullURL, callback, mode);
+  chromeWindow.SocialChatBar.openChat(provider, fullURI.spec, callback, mode);
   // getAttention is ignored if the target window is already foreground, so
   // we can call it unconditionally.
   chromeWindow.getAttention();
diff --git a/toolkit/components/social/SocialService.jsm b/toolkit/components/social/SocialService.jsm
index 9aba7a3c54b1..1d0f8663d43e 100644
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -246,6 +246,8 @@ function SocialProvider(input) {
   this.workerURL = input.workerURL;
   this.sidebarURL = input.sidebarURL;
   this.origin = input.origin;
+  let originUri = Services.io.newURI(input.origin, null, null);
+  this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
   this.ambientNotificationIcons = {};
   this.errorState = null;
   this._active = ActiveProviders.has(this.origin);
@@ -336,14 +338,16 @@ SocialProvider.prototype = {
         reportError('images["' + sub + '"] is missing or not a non-empty string');
         return;
       }
-      // resolve potentially relative URLs then check the scheme is acceptable.
-      url = Services.io.newURI(this.origin, null, null).resolve(url);
-      let uri = Services.io.newURI(url, null, null);
-      if (!uri.schemeIs("http") && !uri.schemeIs("https") && !uri.schemeIs("data")) {
-        reportError('images["' + sub + '"] does not have a valid scheme');
+      // resolve potentially relative URLs but there is no same-origin check
+      // for images to help providers utilize content delivery networks...
+      // Also note no scheme checks are necessary - even a javascript: URL
+      // is safe as gecko evaluates them in a sandbox.
+      let imgUri = this.resolveUri(url);
+      if (!imgUri) {
+        reportError('images["' + sub + '"] is an invalid URL');
         return;
       }
-      promptImages[sub] = url;
+      promptImages[sub] = imgUri.spec;
     }
     for (let sub of ["shareTooltip", "unshareTooltip",
                      "sharedLabel", "unsharedLabel", "unshareLabel",
@@ -451,5 +455,52 @@ SocialProvider.prototype = {
       return null;
     return getFrameWorkerHandle(this.workerURL, window,
                                 "SocialProvider:" + this.origin, this.origin).port;
+  },
+
+  /**
+   * Checks if a given URI is of the same origin as the provider.
+   *
+   * Returns true or false.
+   *
+   * @param {URI or string} uri
+   */
+  isSameOrigin: function isSameOrigin(uri, allowIfInheritsPrincipal) {
+    if (!uri)
+      return false;
+    if (typeof uri == "string") {
+      try {
+        uri = Services.io.newURI(uri, null, null);
+      } catch (ex) {
+        // an invalid URL can't be loaded!
+        return false;
+      }
+    }
+    try {
+      this.principal.checkMayLoad(
+        uri, // the thing to check.
+        false, // reportError - we do our own reporting when necessary.
+        allowIfInheritsPrincipal
+      );
+      return true;
+    } catch (ex) {
+      return false;
+    }
+  },
+
+  /**
+   * Resolve partial URLs for a provider.
+   *
+   * Returns nsIURI object or null on failure
+   *
+   * @param {string} url
+   */
+  resolveUri: function resolveUri(url) {
+    try {
+      let fullURL = this.principal.URI.resolve(url);
+      return Services.io.newURI(fullURL, null, null);
+    } catch (ex) {
+      Cu.reportError("mozSocial: failed to resolve window URL: " + url + "; " + ex);
+      return null;
+    }
   }
 }
diff --git a/toolkit/components/social/WorkerAPI.jsm b/toolkit/components/social/WorkerAPI.jsm
index 3fc1e23366c8..ac9facccffc9 100644
--- a/toolkit/components/social/WorkerAPI.jsm
+++ b/toolkit/components/social/WorkerAPI.jsm
@@ -102,19 +102,18 @@ WorkerAPI.prototype = {
             case "link":
               // if there is a url, make it open a tab
               if (actionArgs.toURL) {
-                try {
-                  let pUri = Services.io.newURI(provider.origin, null, null);
-                  let nUri = Services.io.newURI(pUri.resolve(actionArgs.toURL),
-                                                null, null);
-                  // fixup
-                  if (nUri.scheme != pUri.scheme)
-                    nUri.scheme = pUri.scheme;
-                  if (nUri.prePath == provider.origin) {
-                    let xulWindow = Services.wm.getMostRecentWindow("navigator:browser");
-                    xulWindow.openUILinkIn(nUri.spec, "tab");
-                  }
-                } catch(e) {
-                  Cu.reportError("social.notification-create error: "+e);
+                let uriToOpen = provider.resolveUri(actionArgs.toURL);
+                // Bug 815970 - facebook gives us http:// links even though
+                // the origin is https:// - so we perform a fixup here.
+                let pUri = Services.io.newURI(provider.origin, null, null);
+                if (uriToOpen.scheme != pUri.scheme)
+                  uriToOpen.scheme = pUri.scheme;
+                if (provider.isSameOrigin(uriToOpen)) {
+                  let xulWindow = Services.wm.getMostRecentWindow("navigator:browser");
+                  xulWindow.openUILinkIn(uriToOpen.spec, "tab");
+                } else {
+                  Cu.reportError("Not opening notification link " + actionArgs.toURL
+                                 + " as not in provider origin");
                 }
               }
               break;
diff --git a/toolkit/components/social/test/browser/browser_notifications.js b/toolkit/components/social/test/browser/browser_notifications.js
index c635f63c3d24..420f2085b309 100644
--- a/toolkit/components/social/test/browser/browser_notifications.js
+++ b/toolkit/components/social/test/browser/browser_notifications.js
@@ -6,70 +6,6 @@ const TEST_PROVIDER_ORIGIN = 'http://example.com';
 
 Cu.import("resource://gre/modules/Services.jsm");
 
-// A mock notifications server.  Based on:
-// dom/tests/mochitest/notification/notification_common.js
-const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
-    getService(Ci.nsIUUIDGenerator).generateUUID();
-
-const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
-const ALERTS_SERVICE_CID = Components.ID(Cc[ALERTS_SERVICE_CONTRACT_ID].number);
-
-function MockAlertsService() {}
-
-MockAlertsService.prototype = {
-
-    showAlertNotification: function(imageUrl, title, text, textClickable,
-                                    cookie, alertListener, name) {
-        let obData = JSON.stringify({
-          imageUrl: imageUrl,
-          title: title,
-          text:text,
-          textClickable: textClickable,
-          cookie: cookie,
-          name: name
-        });
-        Services.obs.notifyObservers(null, "social-test:notification-alert", obData);
-
-        if (textClickable) {
-          // probably should do this async....
-          alertListener.observe(null, "alertclickcallback", cookie);
-        }
-
-        alertListener.observe(null, "alertfinished", cookie);
-    },
-
-    QueryInterface: function(aIID) {
-        if (aIID.equals(Ci.nsISupports) ||
-            aIID.equals(Ci.nsIAlertsService))
-            return this;
-        throw Cr.NS_ERROR_NO_INTERFACE;
-    }
-};
-
-var factory = {
-    createInstance: function(aOuter, aIID) {
-        if (aOuter != null)
-            throw Cr.NS_ERROR_NO_AGGREGATION;
-        return new MockAlertsService().QueryInterface(aIID);
-    }
-};
-
-function replacePromptService() {
-  Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
-            .registerFactory(FAKE_CID, "",
-                             ALERTS_SERVICE_CONTRACT_ID,
-                             factory)
-}
-
-function restorePromptService() {
-  Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
-            .registerFactory(ALERTS_SERVICE_CID, "",
-                             ALERTS_SERVICE_CONTRACT_ID,
-                             null);
-}
-// end of alerts service mock.
-
-
 function ensureProvider(workerFunction, cb) {
   let manifest = {
     origin: TEST_PROVIDER_ORIGIN,
@@ -90,8 +26,8 @@ function test() {
   let cbPostTest = function(cb) {
     SocialService.removeProvider(TEST_PROVIDER_ORIGIN, function() {cb()});
   };
-  replacePromptService();
-  registerCleanupFunction(restorePromptService);
+  replaceAlertsService();
+  registerCleanupFunction(restoreAlertsService);
   runTests(tests, undefined, cbPostTest);
 }
 
diff --git a/toolkit/components/social/test/browser/browser_workerAPI.js b/toolkit/components/social/test/browser/browser_workerAPI.js
index b167f99defd8..bf214e85db97 100644
--- a/toolkit/components/social/test/browser/browser_workerAPI.js
+++ b/toolkit/components/social/test/browser/browser_workerAPI.js
@@ -7,6 +7,9 @@ let provider;
 function test() {
   waitForExplicitFinish();
 
+  replaceAlertsService();
+  registerCleanupFunction(restoreAlertsService);
+
   let manifest = {
     origin: 'http://example.com',
     name: "Example Provider",
@@ -144,5 +147,62 @@ let tests = {
       }
     }
     port.postMessage({topic: "test-initialization"});
-  }
+  },
+
+  testNotificationLinks: function(next) {
+    let port = provider.getWorkerPort();
+    let data = {
+      id: 'an id',
+      body: 'the text',
+      action: 'link',
+      actionArgs: {} // will get a toURL elt during the tests...
+    }
+    let testArgs = [
+      // toURL,                 expectedLocation,     expectedWhere]
+      ["http://example.com",      "http://example.com/", "tab"],
+      // bug 815970 - test that a mis-matched scheme gets patched up.
+      ["https://example.com",     "http://example.com/", "tab"],
+      // check an off-origin link is not opened.
+      ["https://mochitest:8888/", null,                 null]
+    ];
+
+    // we monkey-patch openUILinkIn
+    let oldopenUILinkIn = window.openUILinkIn;
+    registerCleanupFunction(function () {
+      // restore the monkey-patch
+      window.openUILinkIn = oldopenUILinkIn;
+    });
+    let openLocation;
+    let openWhere;
+    window.openUILinkIn = function(location, where) {
+      openLocation = location;
+      openWhere = where;
+    }
+
+    // the testing framework.
+    let toURL, expectedLocation, expectedWhere;
+    function nextTest() {
+      if (testArgs.length == 0) {
+        port.close();
+        next(); // all out of tests!
+        return;
+      }
+      openLocation = openWhere = null;
+      [toURL, expectedLocation, expectedWhere] = testArgs.shift();
+      data.actionArgs.toURL = toURL;
+      port.postMessage({topic: 'test-notification-create', data: data});
+    };
+
+    port.onmessage = function(evt) {
+      if (evt.data.topic == "did-notification-create") {
+        is(openLocation, expectedLocation, "url actually opened was " + openLocation);
+        is(openWhere, expectedWhere, "the url was opened in a " + expectedWhere);
+        nextTest();
+      }
+    }
+    // and kick off the tests.
+    port.postMessage({topic: "test-initialization"});
+    nextTest();
+  },
+
 };
diff --git a/toolkit/components/social/test/browser/head.js b/toolkit/components/social/test/browser/head.js
index e0708691aa79..c63f8fe774c6 100644
--- a/toolkit/components/social/test/browser/head.js
+++ b/toolkit/components/social/test/browser/head.js
@@ -59,3 +59,66 @@ function runTests(tests, cbPreTest, cbPostTest, cbFinish) {
   }
   runNextTest();
 }
+
+// A mock notifications server.  Based on:
+// dom/tests/mochitest/notification/notification_common.js
+const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
+    getService(Ci.nsIUUIDGenerator).generateUUID();
+
+const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
+const ALERTS_SERVICE_CID = Components.ID(Cc[ALERTS_SERVICE_CONTRACT_ID].number);
+
+function MockAlertsService() {}
+
+MockAlertsService.prototype = {
+
+    showAlertNotification: function(imageUrl, title, text, textClickable,
+                                    cookie, alertListener, name) {
+        let obData = JSON.stringify({
+          imageUrl: imageUrl,
+          title: title,
+          text:text,
+          textClickable: textClickable,
+          cookie: cookie,
+          name: name
+        });
+        Services.obs.notifyObservers(null, "social-test:notification-alert", obData);
+
+        if (textClickable) {
+          // probably should do this async....
+          alertListener.observe(null, "alertclickcallback", cookie);
+        }
+
+        alertListener.observe(null, "alertfinished", cookie);
+    },
+
+    QueryInterface: function(aIID) {
+        if (aIID.equals(Ci.nsISupports) ||
+            aIID.equals(Ci.nsIAlertsService))
+            return this;
+        throw Cr.NS_ERROR_NO_INTERFACE;
+    }
+};
+
+var factory = {
+    createInstance: function(aOuter, aIID) {
+        if (aOuter != null)
+            throw Cr.NS_ERROR_NO_AGGREGATION;
+        return new MockAlertsService().QueryInterface(aIID);
+    }
+};
+
+function replaceAlertsService() {
+  Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+            .registerFactory(FAKE_CID, "",
+                             ALERTS_SERVICE_CONTRACT_ID,
+                             factory)
+}
+
+function restoreAlertsService() {
+  Components.manager.QueryInterface(Ci.nsIComponentRegistrar)
+            .registerFactory(ALERTS_SERVICE_CID, "",
+                             ALERTS_SERVICE_CONTRACT_ID,
+                             null);
+}
+// end of alerts service mock.
diff --git a/toolkit/components/social/test/browser/worker_social.js b/toolkit/components/social/test/browser/worker_social.js
index 700bc27e8c30..c45ed6daa724 100644
--- a/toolkit/components/social/test/browser/worker_social.js
+++ b/toolkit/components/social/test/browser/worker_social.js
@@ -42,6 +42,11 @@ onconnect = function(e) {
         // start
         apiPort.postMessage({topic: 'social.reload-worker'});
         break;
+      case "test-notification-create":
+        apiPort.postMessage({topic: 'social.notification-create',
+                             data: data});
+        testerPort.postMessage({topic: 'did-notification-create'});
+        break;
     }
   }
   // used for "test-reload-worker"
diff --git a/toolkit/components/social/test/xpcshell/test_SocialService.js b/toolkit/components/social/test/xpcshell/test_SocialService.js
index fdc224add219..1136d8dc6e65 100644
--- a/toolkit/components/social/test/xpcshell/test_SocialService.js
+++ b/toolkit/components/social/test/xpcshell/test_SocialService.js
@@ -32,6 +32,8 @@ function run_test() {
   runner.appendIterator(testGetProviderList(manifests, next));
   runner.appendIterator(testEnabled(manifests, next));
   runner.appendIterator(testAddRemoveProvider(manifests, next));
+  runner.appendIterator(testIsSameOrigin(manifests, next));
+  runner.appendIterator(testResolveUri  (manifests, next));
   runner.next();
 }
 
@@ -136,3 +138,31 @@ function testAddRemoveProvider(manifests, next) {
   newProvider = yield SocialService.getProvider(newProvider.origin, next);
   do_check_true(!newProvider);
 }
+
+function testIsSameOrigin(manifests, next) {
+  let providers = yield SocialService.getProviderList(next);
+  let provider = providers[0];
+  // provider.origin is a string.
+  do_check_true(provider.isSameOrigin(provider.origin));
+  do_check_true(provider.isSameOrigin(Services.io.newURI(provider.origin, null, null)));
+  do_check_true(provider.isSameOrigin(provider.origin + "/some-sub-page"));
+  do_check_true(provider.isSameOrigin(Services.io.newURI(provider.origin + "/some-sub-page", null, null)));
+  do_check_false(provider.isSameOrigin("http://something.com"));
+  do_check_false(provider.isSameOrigin(Services.io.newURI("http://something.com", null, null)));
+  do_check_false(provider.isSameOrigin("data:text/html,

hi")); + do_check_true(provider.isSameOrigin("data:text/html,

hi", true)); + do_check_false(provider.isSameOrigin(Services.io.newURI("data:text/html,

hi", null, null))); + do_check_true(provider.isSameOrigin(Services.io.newURI("data:text/html,

hi", null, null), true)); + // we explicitly handle null and return false + do_check_false(provider.isSameOrigin(null)); +} + +function testResolveUri(manifests, next) { + let providers = yield SocialService.getProviderList(next); + let provider = providers[0]; + do_check_eq(provider.resolveUri(provider.origin).spec, provider.origin + "/"); + do_check_eq(provider.resolveUri("foo.html").spec, provider.origin + "/foo.html"); + do_check_eq(provider.resolveUri("/foo.html").spec, provider.origin + "/foo.html"); + do_check_eq(provider.resolveUri("http://somewhereelse.com/foo.html").spec, "http://somewhereelse.com/foo.html"); + do_check_eq(provider.resolveUri("data:text/html,

hi").spec, "data:text/html,

hi"); +} diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index ab39a01ea354..e0c50ca540f2 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -28,6 +28,20 @@ "n_buckets": 50, "description": "Time spent on one cycle collection (ms)" }, + "CYCLE_COLLECTOR_FULL": { + "kind": "exponential", + "high": "10000", + "n_buckets": 50, + "description": "Full pause time for one cycle collection, including preparation (ms)" + }, + "CYCLE_COLLECTOR_FINISH_IGC": { + "kind": "boolean", + "description": "Cycle collection finished an incremental GC" + }, + "CYCLE_COLLECTOR_SYNC_SKIPPABLE": { + "kind": "boolean", + "description": "Cycle collection synchronously ran forget skippable" + }, "CYCLE_COLLECTOR_VISITED_REF_COUNTED": { "kind": "exponential", "high": "300000", @@ -2259,12 +2273,6 @@ "n_buckets": 10, "description": "Time to flush dirty entries from the cache (ms)" }, - "LOCALDOMSTORAGE_PERCENT_FLUSHED": { - "kind": "linear", - "high": "100", - "n_buckets": 12, - "description": "Percentage of dirty entries during a flush" - }, "LOCALDOMSTORAGE_KEY_SIZE_BYTES": { "kind": "exponential", "low": 1024, @@ -2511,5 +2519,29 @@ "high": "5000", "n_buckets": 10, "description": "Time (ms) it takes to run memory reporters when sending a telemetry ping" + }, + "SSL_SUCCESFUL_CERT_VALIDATION_TIME_LIBPKIX" : { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "description": "Time spent on a successful cert verification in libpix mode (ms)" + }, + "SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC" : { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "description": "Time spent on a successful cert verification in classic mode (ms)" + }, + "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX" : { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "description": "Time spent on an initially failed cert verification in libpix mode (ms)" + }, + "SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_CLASSIC" : { + "kind": "exponential", + "high": "60000", + "n_buckets": 50, + "description": "Time spent on an initially failed cert verification in classic mode (ms)" } } diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 30984afe5b7c..32252b514e22 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -329,7 +329,7 @@ private: TelemetryImpl* TelemetryImpl::sTelemetry = NULL; -NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(TelemetryMallocSizeOf, "telemetry") +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(TelemetryMallocSizeOf) size_t TelemetryImpl::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) @@ -527,20 +527,26 @@ ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h, if (!(JS_DefineProperty(cx, obj, "min", INT_TO_JSVAL(h->declared_min()), NULL, NULL, JSPROP_ENUMERATE) && JS_DefineProperty(cx, obj, "max", INT_TO_JSVAL(h->declared_max()), NULL, NULL, JSPROP_ENUMERATE) && JS_DefineProperty(cx, obj, "histogram_type", INT_TO_JSVAL(h->histogram_type()), NULL, NULL, JSPROP_ENUMERATE) - && JS_DefineProperty(cx, obj, "sum", DOUBLE_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE) - && JS_DefineProperty(cx, obj, "log_sum", DOUBLE_TO_JSVAL(ss.log_sum()), NULL, NULL, JSPROP_ENUMERATE) - && JS_DefineProperty(cx, obj, "log_sum_squares", DOUBLE_TO_JSVAL(ss.log_sum_squares()), NULL, NULL, JSPROP_ENUMERATE))) { + && JS_DefineProperty(cx, obj, "sum", DOUBLE_TO_JSVAL(ss.sum()), NULL, NULL, JSPROP_ENUMERATE))) { return REFLECT_FAILURE; } - // Export |sum_squares| as two separate 32-bit properties so that we - // can accurately reconstruct it on the analysis side. - uint64_t sum_squares = ss.sum_squares(); - uint32_t lo = sum_squares; - uint32_t hi = sum_squares >> 32; - if (!(JS_DefineProperty(cx, obj, "sum_squares_lo", INT_TO_JSVAL(lo), NULL, NULL, JSPROP_ENUMERATE) - && JS_DefineProperty(cx, obj, "sum_squares_hi", INT_TO_JSVAL(hi), NULL, NULL, JSPROP_ENUMERATE))) { - return REFLECT_FAILURE; + if (h->histogram_type() == Histogram::HISTOGRAM) { + if (!(JS_DefineProperty(cx, obj, "log_sum", DOUBLE_TO_JSVAL(ss.log_sum()), NULL, NULL, JSPROP_ENUMERATE) + && JS_DefineProperty(cx, obj, "log_sum_squares", DOUBLE_TO_JSVAL(ss.log_sum_squares()), NULL, NULL, JSPROP_ENUMERATE))) { + return REFLECT_FAILURE; + } + } else { + // Export |sum_squares| as two separate 32-bit properties so that we + // can accurately reconstruct it on the analysis side. + uint64_t sum_squares = ss.sum_squares(); + // Cast to avoid implicit truncation warnings. + uint32_t lo = static_cast(sum_squares); + uint32_t hi = static_cast(sum_squares >> 32); + if (!(JS_DefineProperty(cx, obj, "sum_squares_lo", INT_TO_JSVAL(lo), NULL, NULL, JSPROP_ENUMERATE) + && JS_DefineProperty(cx, obj, "sum_squares_hi", INT_TO_JSVAL(hi), NULL, NULL, JSPROP_ENUMERATE))) { + return REFLECT_FAILURE; + } } const size_t count = h->bucket_count(); diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 6b056cd176ca..56a4e265efad 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -255,12 +255,17 @@ TelemetryPing.prototype = { bucket_count: r.length, histogram_type: hgram.histogram_type, values: {}, - sum: hgram.sum, - sum_squares_lo: hgram.sum_squares_lo, - sum_squares_hi: hgram.sum_squares_hi, - log_sum: hgram.log_sum, - log_sum_squares: hgram.log_sum_squares + sum: hgram.sum }; + + if (hgram.histogram_type == Telemetry.HISTOGRAM_EXPONENTIAL) { + retgram.log_sum = hgram.log_sum; + retgram.log_sum_squares = hgram.log_sum_squares; + } else { + retgram.sum_squares_lo = hgram.sum_squares_lo; + retgram.sum_squares_hi = hgram.sum_squares_hi; + } + let first = true; let last = 0; @@ -1072,7 +1077,7 @@ TelemetryPing.prototype = { }, classID: Components.ID("{55d6a5fa-130e-4ee6-a158-0133af3b86ba}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsITelemetryPing]), + QueryInterface: XPCOMUtils.generateQI([Ci.nsITelemetryPing, Ci.nsIObserver]), }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TelemetryPing]); diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index aa26d7629ba5..e50e3b2629e3 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -211,9 +211,7 @@ function checkPayload(request, reason, successfulPings) { values: {0:1, 1:0}, sum: 0, sum_squares_lo: 0, - sum_squares_hi: 0, - log_sum: 0, - log_sum_squares: 0 + sum_squares_hi: 0 }; let flag = payload.histograms[TELEMETRY_TEST_FLAG]; do_check_eq(uneval(flag), uneval(expected_flag)); @@ -226,9 +224,7 @@ function checkPayload(request, reason, successfulPings) { values: {0:1, 1:successfulPings, 2:0}, sum: successfulPings, sum_squares_lo: successfulPings, - sum_squares_hi: 0, - log_sum: 0, - log_sum_squares: 0 + sum_squares_hi: 0 }; let tc = payload.histograms[TELEMETRY_SUCCESS]; do_check_eq(uneval(tc), uneval(expected_tc)); diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index 86ab0319cf1f..6a66fe39753f 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -27,13 +27,20 @@ function test_histogram(histogram_type, name, min, max, bucket_count) { h.add(v); } var s = h.snapshot(); - // verify sum + // verify properties do_check_eq(sum, s.sum); - // Doing the math to verify sum_squares was reflected correctly is - // tedious in JavaScript. Just make sure we have something. - do_check_neq(s.sum_squares_lo + s.sum_squares_hi, 0); - do_check_eq(log_sum, s.log_sum); - do_check_eq(log_sum_squares, s.log_sum_squares); + if (histogram_type == Telemetry.HISTOGRAM_EXPONENTIAL) { + do_check_eq(log_sum, s.log_sum); + do_check_eq(log_sum_squares, s.log_sum_squares); + do_check_false("sum_squares_lo" in s); + do_check_false("sum_squares_hi" in s); + } else { + // Doing the math to verify sum_squares was reflected correctly is + // tedious in JavaScript. Just make sure we have something. + do_check_neq(s.sum_squares_lo + s.sum_squares_hi, 0); + do_check_false("log_sum" in s); + do_check_false("log_sum_squares" in s); + } // there should be exactly one element per bucket for each(var i in s.counts) { @@ -60,6 +67,13 @@ function test_histogram(histogram_type, name, min, max, bucket_count) { do_check_eq(i, 0); } do_check_eq(s.sum, 0); + if (histogram_type == Telemetry.HISTOGRAM_EXPONENTIAL) { + do_check_eq(s.log_sum, 0); + do_check_eq(s.log_sum_squares, 0); + } else { + do_check_eq(s.sum_squares_lo, 0); + do_check_eq(s.sum_squares_hi, 0); + } h.add(0); h.add(1); @@ -160,10 +174,13 @@ function compareHistograms(h1, h2) { do_check_eq(s1.min, s2.min); do_check_eq(s1.max, s2.max); do_check_eq(s1.sum, s2.sum); - do_check_eq(s1.sum_squares_lo, s2.sum_squares_lo); - do_check_eq(s1.sum_squares_hi, s2.sum_squares_hi); - do_check_eq(s1.log_sum, s2.log_sum); - do_check_eq(s1.log_sum_squares, s2.log_sum_squares); + if (s1.histogram_type == Telemetry.HISTOGRAM_EXPONENTIAL) { + do_check_eq(s1.log_sum, s2.log_sum); + do_check_eq(s1.log_sum_squares, s2.log_sum_squares); + } else { + do_check_eq(s1.sum_squares_lo, s2.sum_squares_lo); + do_check_eq(s1.sum_squares_hi, s2.sum_squares_hi); + } do_check_eq(s1.counts.length, s2.counts.length); for (let i = 0; i < s1.counts.length; i++) diff --git a/toolkit/components/url-classifier/Classifier.cpp b/toolkit/components/url-classifier/Classifier.cpp index 0a71187d529d..1df8598b57e4 100644 --- a/toolkit/components/url-classifier/Classifier.cpp +++ b/toolkit/components/url-classifier/Classifier.cpp @@ -778,7 +778,7 @@ Classifier::GetLookupCache(const nsACString& aTable) nsresult Classifier::ReadNoiseEntries(const Prefix& aPrefix, const nsACString& aTableName, - int32_t aCount, + uint32_t aCount, PrefixArray* aNoiseEntries) { LookupCache *cache = GetLookupCache(aTableName); @@ -790,7 +790,7 @@ Classifier::ReadNoiseEntries(const Prefix& aPrefix, nsresult rv = cache->GetPrefixes(&prefixes); NS_ENSURE_SUCCESS(rv, rv); - int32_t idx = prefixes.BinaryIndexOf(aPrefix.ToUint32()); + uint32_t idx = prefixes.BinaryIndexOf(aPrefix.ToUint32()); if (idx == nsTArray::NoIndex) { NS_WARNING("Could not find prefix in PrefixSet during noise lookup"); @@ -799,7 +799,7 @@ Classifier::ReadNoiseEntries(const Prefix& aPrefix, idx -= idx % aCount; - for (int32_t i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) { + for (uint32_t i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) { Prefix newPref; newPref.FromUint32(prefixes[idx+i]); if (newPref != aPrefix) { @@ -810,5 +810,5 @@ Classifier::ReadNoiseEntries(const Prefix& aPrefix, return NS_OK; } -} -} +} // namespace safebrowsing +} // namespace mozilla diff --git a/toolkit/components/url-classifier/Classifier.h b/toolkit/components/url-classifier/Classifier.h index fe0cbb906a37..53121bf76c33 100644 --- a/toolkit/components/url-classifier/Classifier.h +++ b/toolkit/components/url-classifier/Classifier.h @@ -67,7 +67,7 @@ public: */ nsresult ReadNoiseEntries(const Prefix& aPrefix, const nsACString& aTableName, - int32_t aCount, + uint32_t aCount, PrefixArray* aNoiseEntries); private: void DropStores(); diff --git a/toolkit/components/url-classifier/Entries.h b/toolkit/components/url-classifier/Entries.h index 869d6f8539ea..3a03a28db543 100644 --- a/toolkit/components/url-classifier/Entries.h +++ b/toolkit/components/url-classifier/Entries.h @@ -10,16 +10,6 @@ #include "nsString.h" #include "nsICryptoHash.h" #include "nsNetUtil.h" -#include "prlog.h" - -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (false) -#endif #if DEBUG #include "plbase64.h" @@ -280,6 +270,6 @@ WriteTArray(nsIOutputStream* aStream, nsTArray& aArray) &written); } -} -} -#endif +} // namespace safebrowsing +} // namespace mozilla +#endif // SBEntries_h__ diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index 13c0df9963aa..8aab9ccab669 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -70,7 +70,7 @@ PRLogModuleInfo *gUrlClassifierDbServiceLog = nullptr; #define GETHASH_TABLES_PREF "urlclassifier.gethashtables" -#define CONFIRM_AGE_PREF "urlclassifier.confirm-age" +#define CONFIRM_AGE_PREF "urlclassifier.max-complete-age" #define CONFIRM_AGE_DEFAULT_SEC (45 * 60) class nsUrlClassifierDBServiceWorker; @@ -116,7 +116,7 @@ public: NS_DECL_NSIURLCLASSIFIERDBSERVICE NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER - nsresult Init(int32_t gethashNoise, nsCOMPtr aCacheDir, + nsresult Init(uint32_t aGethashNoise, nsCOMPtr aCacheDir, bool aPerClientRandomize); // Queue a lookup for the worker to perform, called in the main thread. @@ -150,7 +150,7 @@ private: nsresult AddNoise(const Prefix aPrefix, const nsCString tableName, - int32_t aCount, + uint32_t aCount, LookupResultArray& results); nsCOMPtr mCryptoHash; @@ -184,7 +184,7 @@ private: uint32_t mHashKey; // The number of noise entries to add to the set of lookup results. - int32_t mGethashNoise; + uint32_t mGethashNoise; // Randomize clients with a key or not. bool mPerClientRandomize; @@ -224,11 +224,11 @@ nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker() } nsresult -nsUrlClassifierDBServiceWorker::Init(int32_t gethashNoise, +nsUrlClassifierDBServiceWorker::Init(uint32_t aGethashNoise, nsCOMPtr aCacheDir, bool aPerClientRandomize) { - mGethashNoise = gethashNoise; + mGethashNoise = aGethashNoise; mCacheDir = aCacheDir; mPerClientRandomize = aPerClientRandomize; @@ -357,7 +357,7 @@ nsUrlClassifierDBServiceWorker::HandlePendingLookups() nsresult nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix, const nsCString tableName, - int32_t aCount, + uint32_t aCount, LookupResultArray& results) { if (aCount < 1) { @@ -1129,7 +1129,7 @@ nsUrlClassifierDBService::Init() // Should we check document loads for malware URIs? nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); - int32_t gethashNoise = 0; + uint32_t gethashNoise = 0; if (prefs) { bool tmpbool; rv = prefs->GetBoolPref(CHECK_MALWARE_PREF, &tmpbool); @@ -1142,9 +1142,10 @@ nsUrlClassifierDBService::Init() prefs->AddObserver(CHECK_PHISHING_PREF, this, false); - if (NS_FAILED(prefs->GetIntPref(GETHASH_NOISE_PREF, &gethashNoise))) { - gethashNoise = GETHASH_NOISE_DEFAULT; - } + int32_t tmpint; + rv = prefs->GetIntPref(GETHASH_NOISE_PREF, &tmpint); + gethashNoise = (NS_SUCCEEDED(rv) && tmpint >= 0) ? + static_cast(tmpint) : GETHASH_NOISE_DEFAULT; nsXPIDLCString tmpstr; if (NS_SUCCEEDED(prefs->GetCharPref(GETHASH_TABLES_PREF, getter_Copies(tmpstr)))) { @@ -1153,7 +1154,6 @@ nsUrlClassifierDBService::Init() prefs->AddObserver(GETHASH_TABLES_PREF, this, false); - int32_t tmpint; rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint); PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC); diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp index 03bb0aa161f5..58f53b9828f2 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp @@ -49,8 +49,7 @@ private: NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter) -NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf, - "storage/prefixset") +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf) nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, const nsACString& aName) diff --git a/toolkit/components/url-classifier/tests/unit/test_partial.js b/toolkit/components/url-classifier/tests/unit/test_partial.js index 69ba49af2983..fc3600409c66 100644 --- a/toolkit/components/url-classifier/tests/unit/test_partial.js +++ b/toolkit/components/url-classifier/tests/unit/test_partial.js @@ -743,7 +743,7 @@ function testStaleList() }; // Consider a match stale after one second. - prefBranch.setIntPref("urlclassifier.confirm-age", 1); + prefBranch.setIntPref("urlclassifier.max-complete-age", 1); // Apply the update. doStreamUpdate(update, function() { @@ -752,7 +752,7 @@ function testStaleList() new Timer(3000, function() { // Now the lists should be marked stale. Check assertions. checkAssertions(assertions, function() { - prefBranch.setIntPref("urlclassifier.confirm-age", 2700); + prefBranch.setIntPref("urlclassifier.max-complete-age", 2700); runNextTest(); }); }, updateError); @@ -783,7 +783,7 @@ function testStaleListEmpty() }; // Consider a match stale after one second. - prefBranch.setIntPref("urlclassifier.confirm-age", 1); + prefBranch.setIntPref("urlclassifier.max-complete-age", 1); // Apply the update. doStreamUpdate(update, function() { @@ -792,7 +792,7 @@ function testStaleListEmpty() new Timer(3000, function() { // Now the lists should be marked stale. Check assertions. checkAssertions(assertions, function() { - prefBranch.setIntPref("urlclassifier.confirm-age", 2700); + prefBranch.setIntPref("urlclassifier.max-complete-age", 2700); runNextTest(); }); }, updateError); diff --git a/toolkit/content/PrivateBrowsingUtils.jsm b/toolkit/content/PrivateBrowsingUtils.jsm index 1d524edbcd3d..edad45dc93f9 100644 --- a/toolkit/content/PrivateBrowsingUtils.jsm +++ b/toolkit/content/PrivateBrowsingUtils.jsm @@ -50,19 +50,3 @@ this.PrivateBrowsingUtils = { } }; -#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING -function autoStartObserver(aSubject, aTopic, aData) { - var newValue = Services.prefs.getBoolPref(kAutoStartPref); - var windowsEnum = Services.wm.getEnumerator(null); - while (windowsEnum.hasMoreElements()) { - var window = windowsEnum.getNext(); - window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsILoadContext) - .usePrivateBrowsing = newValue; - } -} - -Services.prefs.addObserver(kAutoStartPref, autoStartObserver, false); -#endif - diff --git a/toolkit/content/aboutTelemetry.js b/toolkit/content/aboutTelemetry.js index 4251f2a5a0d7..b722835341dc 100644 --- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -218,14 +218,60 @@ let SlowSQL = { } }; -let ChromeHangs = { +/** + * Removes child elements from the supplied div + * + * @param aDiv Element to be cleared + */ +function clearDivData(aDiv) { + while (aDiv.hasChildNodes()) { + aDiv.removeChild(aDiv.lastChild); + } +}; - symbolRequest: null, +let StackRenderer = { stackTitle: bundle.GetStringFromName("stackTitle"), memoryMapTitle: bundle.GetStringFromName("memoryMapTitle"), + /** + * Outputs the memory map associated with this hang report + * + * @param aDiv Output div + */ + renderMemoryMap: function StackRenderer_renderMemoryMap(aDiv, memoryMap) { + aDiv.appendChild(document.createTextNode(this.memoryMapTitle)); + aDiv.appendChild(document.createElement("br")); + + for (let currentModule of memoryMap) { + aDiv.appendChild(document.createTextNode(currentModule.join(" "))); + aDiv.appendChild(document.createElement("br")); + } + + aDiv.appendChild(document.createElement("br")); + }, + + /** + * Outputs the raw PCs from the hang's stack + * + * @param aDiv Output div + * @param aStack Array of PCs from the hang stack + */ + renderStack: function StackRenderer_renderStack(aDiv, aStack) { + aDiv.appendChild(document.createTextNode(this.stackTitle)); + let stackText = " " + aStack.join(" "); + aDiv.appendChild(document.createTextNode(stackText)); + + aDiv.appendChild(document.createElement("br")); + aDiv.appendChild(document.createElement("br")); + } +}; + +let ChromeHangs = { + + symbolRequest: null, + errorMessage: bundle.GetStringFromName("errorFetchingSymbols"), /** @@ -233,7 +279,7 @@ let ChromeHangs = { */ render: function ChromeHangs_render() { let hangsDiv = document.getElementById("chrome-hangs-data"); - this.clearHangData(hangsDiv); + clearDivData(hangsDiv); document.getElementById("fetch-symbols").classList.remove("hidden"); document.getElementById("hide-symbols").classList.add("hidden"); @@ -244,13 +290,14 @@ let ChromeHangs = { return; } - this.renderMemoryMap(hangsDiv); + let memoryMap = hangs.memoryMap; + StackRenderer.renderMemoryMap(hangsDiv, memoryMap); let durations = hangs.durations; for (let i = 0; i < stacks.length; ++i) { let stack = stacks[i]; this.renderHangHeader(hangsDiv, i + 1, durations[i]); - this.renderStack(hangsDiv, stack) + StackRenderer.renderStack(hangsDiv, stack) } }, @@ -273,40 +320,6 @@ let ChromeHangs = { aDiv.appendChild(document.createElement("br")); }, - /** - * Outputs the raw PCs from the hang's stack - * - * @param aDiv Output div - * @param aStack Array of PCs from the hang stack - */ - renderStack: function ChromeHangs_renderStack(aDiv, aStack) { - aDiv.appendChild(document.createTextNode(this.stackTitle)); - let stackText = " " + aStack.join(" "); - aDiv.appendChild(document.createTextNode(stackText)); - - aDiv.appendChild(document.createElement("br")); - aDiv.appendChild(document.createElement("br")); - }, - - /** - * Outputs the memory map associated with this hang report - * - * @param aDiv Output div - */ - renderMemoryMap: function ChromeHangs_renderMemoryMap(aDiv) { - aDiv.appendChild(document.createTextNode(this.memoryMapTitle)); - aDiv.appendChild(document.createElement("br")); - - let hangs = Telemetry.chromeHangs; - let memoryMap = hangs.memoryMap; - for (let currentModule of memoryMap) { - aDiv.appendChild(document.createTextNode(currentModule.join(" "))); - aDiv.appendChild(document.createElement("br")); - } - - aDiv.appendChild(document.createElement("br")); - }, - /** * Sends a symbolication request for the recorded hangs */ @@ -343,7 +356,7 @@ let ChromeHangs = { document.getElementById("hide-symbols").classList.remove("hidden"); let hangsDiv = document.getElementById("chrome-hangs-data"); - this.clearHangData(hangsDiv); + clearDivData(hangsDiv); if (this.symbolRequest.status != 200) { hangsDiv.appendChild(document.createTextNode(this.errorMessage)); @@ -372,17 +385,6 @@ let ChromeHangs = { } hangsDiv.appendChild(document.createElement("br")); } - }, - - /** - * Removes child elements from the supplied div - * - * @param aDiv Element to be cleared - */ - clearHangData: function ChromeHangs_clearHangData(aDiv) { - while (aDiv.hasChildNodes()) { - aDiv.removeChild(aDiv.lastChild); - } } }; @@ -712,13 +714,17 @@ function onLoad() { } // Show addon histogram data - histograms = Telemetry.addonHistogramSnapshots; - if (Object.keys(histograms).length) { - let addonDiv = document.getElementById("addon-histograms"); + let addonDiv = document.getElementById("addon-histograms"); + let addonHistogramsRendered = false; + let addonData = Telemetry.addonHistogramSnapshots; + for (let [addon, histograms] of Iterator(addonData)) { for (let [name, hgram] of Iterator(histograms)) { - Histogram.render(addonDiv, "ADDON_" + name, hgram); + addonHistogramsRendered = true; + Histogram.render(addonDiv, addon + ": " + name, hgram); } - } else { + } + + if (!addonHistogramsRendered) { showEmptySectionMessage("addon-histograms-section"); } diff --git a/toolkit/content/tests/chrome/test_arrowpanel.xul b/toolkit/content/tests/chrome/test_arrowpanel.xul index 53b35ce4367b..43480bef3c43 100644 --- a/toolkit/content/tests/chrome/test_arrowpanel.xul +++ b/toolkit/content/tests/chrome/test_arrowpanel.xul @@ -25,7 +25,7 @@