12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "Accessible-inl.h"
- #include "nsIXBLAccessible.h"
- #include "EmbeddedObjCollector.h"
- #include "AccGroupInfo.h"
- #include "AccIterator.h"
- #include "nsAccUtils.h"
- #include "nsAccessibilityService.h"
- #include "ApplicationAccessible.h"
- #include "NotificationController.h"
- #include "nsEventShell.h"
- #include "nsTextEquivUtils.h"
- #include "DocAccessibleChild.h"
- #include "EventTree.h"
- #include "Relation.h"
- #include "Role.h"
- #include "RootAccessible.h"
- #include "States.h"
- #include "StyleInfo.h"
- #include "TableAccessible.h"
- #include "TableCellAccessible.h"
- #include "TreeWalker.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMNodeFilter.h"
- #include "nsIDOMHTMLElement.h"
- #include "nsIDOMKeyEvent.h"
- #include "nsIDOMTreeWalker.h"
- #include "nsIDOMXULButtonElement.h"
- #include "nsIDOMXULDocument.h"
- #include "nsIDOMXULElement.h"
- #include "nsIDOMXULLabelElement.h"
- #include "nsIDOMXULSelectCntrlEl.h"
- #include "nsIDOMXULSelectCntrlItemEl.h"
- #include "nsPIDOMWindow.h"
- #include "nsIDocument.h"
- #include "nsIContent.h"
- #include "nsIForm.h"
- #include "nsIFormControl.h"
- #include "nsDeckFrame.h"
- #include "nsLayoutUtils.h"
- #include "nsIPersistentProperties2.h"
- #include "nsIPresShell.h"
- #include "nsIStringBundle.h"
- #include "nsPresContext.h"
- #include "nsIFrame.h"
- #include "nsView.h"
- #include "nsIDocShellTreeItem.h"
- #include "nsIScrollableFrame.h"
- #include "nsFocusManager.h"
- #include "nsXPIDLString.h"
- #include "nsUnicharUtils.h"
- #include "nsReadableUtils.h"
- #include "prdtoa.h"
- #include "nsIAtom.h"
- #include "nsIURI.h"
- #include "nsArrayUtils.h"
- #include "nsIMutableArray.h"
- #include "nsIObserverService.h"
- #include "nsIServiceManager.h"
- #include "nsWhitespaceTokenizer.h"
- #include "nsAttrName.h"
- #ifdef DEBUG
- #include "nsIDOMCharacterData.h"
- #endif
- #include "mozilla/Assertions.h"
- #include "mozilla/BasicEvents.h"
- #include "mozilla/EventStates.h"
- #include "mozilla/FloatingPoint.h"
- #include "mozilla/MouseEvents.h"
- #include "mozilla/Unused.h"
- #include "mozilla/Preferences.h"
- #include "mozilla/dom/CanvasRenderingContext2D.h"
- #include "mozilla/dom/Element.h"
- #include "mozilla/dom/HTMLCanvasElement.h"
- #include "mozilla/dom/HTMLBodyElement.h"
- #include "mozilla/dom/TreeWalker.h"
- using namespace mozilla;
- using namespace mozilla::a11y;
- ////////////////////////////////////////////////////////////////////////////////
- // Accessible: nsISupports and cycle collection
- NS_IMPL_CYCLE_COLLECTION_CLASS(Accessible)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Accessible)
- tmp->Shutdown();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Accessible)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent, mDoc)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Accessible)
- if (aIID.Equals(NS_GET_IID(Accessible)))
- foundInterface = this;
- else
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, Accessible)
- NS_INTERFACE_MAP_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(Accessible)
- NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_DESTROY(Accessible, LastRelease())
- Accessible::Accessible(nsIContent* aContent, DocAccessible* aDoc) :
- mContent(aContent), mDoc(aDoc),
- mParent(nullptr), mIndexInParent(-1),
- mRoleMapEntryIndex(aria::NO_ROLE_MAP_ENTRY_INDEX),
- mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
- mReorderEventTarget(false), mShowEventTarget(false), mHideEventTarget(false)
- {
- mBits.groupInfo = nullptr;
- mInt.mIndexOfEmbeddedChild = -1;
- }
- Accessible::~Accessible()
- {
- NS_ASSERTION(!mDoc, "LastRelease was never called!?!");
- }
- ENameValueFlag
- Accessible::Name(nsString& aName)
- {
- aName.Truncate();
- if (!HasOwnContent())
- return eNameOK;
- ARIAName(aName);
- if (!aName.IsEmpty())
- return eNameOK;
- nsCOMPtr<nsIXBLAccessible> xblAccessible(do_QueryInterface(mContent));
- if (xblAccessible) {
- xblAccessible->GetAccessibleName(aName);
- if (!aName.IsEmpty())
- return eNameOK;
- }
- ENameValueFlag nameFlag = NativeName(aName);
- if (!aName.IsEmpty())
- return nameFlag;
- // In the end get the name from tooltip.
- if (mContent->IsHTMLElement()) {
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aName)) {
- aName.CompressWhitespace();
- return eNameFromTooltip;
- }
- } else if (mContent->IsXULElement()) {
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
- aName.CompressWhitespace();
- return eNameFromTooltip;
- }
- } else if (mContent->IsSVGElement()) {
- // 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->IsSVGElement(nsGkAtoms::desc)) {
- nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
- return eNameFromTooltip;
- }
- }
- }
- if (nameFlag != eNoNameOnPurpose)
- aName.SetIsVoid(true);
- return nameFlag;
- }
- void
- Accessible::Description(nsString& aDescription)
- {
- // There are 4 conditions that make an accessible have no accDescription:
- // 1. it's a text node; or
- // 2. It has no DHTML describedby property
- // 3. it doesn't have an accName; or
- // 4. its title attribute already equals to its accName nsAutoString name;
- if (!HasOwnContent() || mContent->IsNodeOfType(nsINode::eTEXT))
- return;
- nsTextEquivUtils::
- GetTextEquivFromIDRefs(this, nsGkAtoms::aria_describedby,
- aDescription);
- if (aDescription.IsEmpty()) {
- NativeDescription(aDescription);
- if (aDescription.IsEmpty()) {
- // Keep the Name() method logic.
- if (mContent->IsHTMLElement()) {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, aDescription);
- } else if (mContent->IsXULElement()) {
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aDescription);
- } else if (mContent->IsSVGElement()) {
- for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
- childElm = childElm->GetNextSibling()) {
- if (childElm->IsSVGElement(nsGkAtoms::desc)) {
- nsTextEquivUtils::AppendTextEquivFromContent(this, childElm,
- &aDescription);
- break;
- }
- }
- }
- }
- }
- if (!aDescription.IsEmpty()) {
- aDescription.CompressWhitespace();
- nsAutoString name;
- Name(name);
- // Don't expose a description if it is the same as the name.
- if (aDescription.Equals(name))
- aDescription.Truncate();
- }
- }
- KeyBinding
- Accessible::AccessKey() const
- {
- if (!HasOwnContent())
- return KeyBinding();
- uint32_t key = nsCoreUtils::GetAccessKeyFor(mContent);
- if (!key && mContent->IsElement()) {
- Accessible* label = nullptr;
- // Copy access key from label node.
- if (mContent->IsHTMLElement()) {
- // Unless it is labeled via an ancestor <label>, in which case that would
- // be redundant.
- HTMLLabelIterator iter(Document(), this,
- HTMLLabelIterator::eSkipAncestorLabel);
- label = iter.Next();
- } else if (mContent->IsXULElement()) {
- XULLabelIterator iter(Document(), mContent);
- label = iter.Next();
- }
- if (label)
- key = nsCoreUtils::GetAccessKeyFor(label->GetContent());
- }
- if (!key)
- return KeyBinding();
- // Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
- switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
- case -1:
- break;
- case nsIDOMKeyEvent::DOM_VK_SHIFT:
- return KeyBinding(key, KeyBinding::kShift);
- case nsIDOMKeyEvent::DOM_VK_CONTROL:
- return KeyBinding(key, KeyBinding::kControl);
- case nsIDOMKeyEvent::DOM_VK_ALT:
- return KeyBinding(key, KeyBinding::kAlt);
- case nsIDOMKeyEvent::DOM_VK_META:
- return KeyBinding(key, KeyBinding::kMeta);
- default:
- return KeyBinding();
- }
- // Determine the access modifier used in this context.
- nsIDocument* document = mContent->GetUncomposedDoc();
- if (!document)
- return KeyBinding();
- nsCOMPtr<nsIDocShellTreeItem> treeItem(document->GetDocShell());
- if (!treeItem)
- return KeyBinding();
- nsresult rv = NS_ERROR_FAILURE;
- int32_t modifierMask = 0;
- switch (treeItem->ItemType()) {
- case nsIDocShellTreeItem::typeChrome:
- rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
- break;
- case nsIDocShellTreeItem::typeContent:
- rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
- break;
- }
- return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
- }
- KeyBinding
- Accessible::KeyboardShortcut() const
- {
- return KeyBinding();
- }
- void
- Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
- {
- nsCOMPtr<nsIStringBundleService> stringBundleService =
- services::GetStringBundleService();
- if (!stringBundleService)
- return;
- nsCOMPtr<nsIStringBundle> stringBundle;
- stringBundleService->CreateBundle(
- "chrome://global-platform/locale/accessible.properties",
- getter_AddRefs(stringBundle));
- if (!stringBundle)
- return;
- nsXPIDLString xsValue;
- nsresult rv = stringBundle->GetStringFromName(aKey.get(), getter_Copies(xsValue));
- if (NS_SUCCEEDED(rv))
- aStringOut.Assign(xsValue);
- }
- uint64_t
- Accessible::VisibilityState()
- {
- nsIFrame* frame = GetFrame();
- if (!frame)
- return states::INVISIBLE;
- // Walk the parent frame chain to see if there's invisible parent or the frame
- // is in background tab.
- if (!frame->StyleVisibility()->IsVisible())
- return states::INVISIBLE;
- nsIFrame* curFrame = frame;
- do {
- nsView* view = curFrame->GetView();
- if (view && view->GetVisibility() == nsViewVisibility_kHide)
- return states::INVISIBLE;
- if (nsLayoutUtils::IsPopup(curFrame))
- return 0;
- // Offscreen state for background tab content and invisible for not selected
- // deck panel.
- nsIFrame* parentFrame = curFrame->GetParent();
- nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
- if (deckFrame && deckFrame->GetSelectedBox() != curFrame) {
- if (deckFrame->GetContent()->IsXULElement(nsGkAtoms::tabpanels))
- return states::OFFSCREEN;
- NS_NOTREACHED("Children of not selected deck panel are not accessible.");
- return states::INVISIBLE;
- }
- // If contained by scrollable frame then check that at least 12 pixels
- // around the object is visible, otherwise the object is offscreen.
- nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
- if (scrollableFrame) {
- nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
- nsRect frameRect = nsLayoutUtils::TransformFrameRectToAncestor(
- frame, frame->GetRectRelativeToSelf(), parentFrame);
- if (!scrollPortRect.Contains(frameRect)) {
- const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
- scrollPortRect.Deflate(kMinPixels, kMinPixels);
- if (!scrollPortRect.Intersects(frameRect))
- return states::OFFSCREEN;
- }
- }
- if (!parentFrame) {
- parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
- if (parentFrame && !parentFrame->StyleVisibility()->IsVisible())
- return states::INVISIBLE;
- }
- curFrame = parentFrame;
- } while (curFrame);
- // Zero area rects can occur in the first frame of a multi-frame text flow,
- // in which case the rendered text is not empty and the frame should not be
- // marked invisible.
- // XXX Can we just remove this check? Why do we need to mark empty
- // text invisible?
- if (frame->GetType() == nsGkAtoms::textFrame &&
- !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
- frame->GetRect().IsEmpty()) {
- nsIFrame::RenderedText text = frame->GetRenderedText(0,
- UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
- nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
- if (text.mString.IsEmpty()) {
- return states::INVISIBLE;
- }
- }
- return 0;
- }
- uint64_t
- Accessible::NativeState()
- {
- uint64_t state = 0;
- if (!IsInDocument())
- state |= states::STALE;
- if (HasOwnContent() && mContent->IsElement()) {
- EventStates elementState = mContent->AsElement()->State();
- if (elementState.HasState(NS_EVENT_STATE_INVALID))
- state |= states::INVALID;
- if (elementState.HasState(NS_EVENT_STATE_REQUIRED))
- state |= states::REQUIRED;
- state |= NativeInteractiveState();
- if (FocusMgr()->IsFocused(this))
- state |= states::FOCUSED;
- }
- // Gather states::INVISIBLE and states::OFFSCREEN flags for this object.
- state |= VisibilityState();
- nsIFrame *frame = GetFrame();
- if (frame) {
- if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW)
- state |= states::FLOATING;
- // XXX we should look at layout for non XUL box frames, but need to decide
- // how that interacts with ARIA.
- if (HasOwnContent() && mContent->IsXULElement() && frame->IsXULBoxFrame()) {
- const nsStyleXUL* xulStyle = frame->StyleXUL();
- if (xulStyle && frame->IsXULBoxFrame()) {
- // In XUL all boxes are either vertical or horizontal
- if (xulStyle->mBoxOrient == StyleBoxOrient::Vertical)
- state |= states::VERTICAL;
- else
- state |= states::HORIZONTAL;
- }
- }
- }
- // Check if a XUL element has the popup attribute (an attached popup menu).
- if (HasOwnContent() && mContent->IsXULElement() &&
- mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
- state |= states::HASPOPUP;
- // Bypass the link states specialization for non links.
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (!roleMapEntry || roleMapEntry->roleRule == kUseNativeRole ||
- roleMapEntry->role == roles::LINK)
- state |= NativeLinkState();
- return state;
- }
- uint64_t
- Accessible::NativeInteractiveState() const
- {
- if (!mContent->IsElement())
- return 0;
- if (NativelyUnavailable())
- return states::UNAVAILABLE;
- nsIFrame* frame = GetFrame();
- if (frame && frame->IsFocusable())
- return states::FOCUSABLE;
- return 0;
- }
- uint64_t
- Accessible::NativeLinkState() const
- {
- return 0;
- }
- bool
- Accessible::NativelyUnavailable() const
- {
- if (mContent->IsHTMLElement())
- return mContent->AsElement()->State().HasState(NS_EVENT_STATE_DISABLED);
- return mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
- nsGkAtoms::_true, eCaseMatters);
- }
- Accessible*
- Accessible::FocusedChild()
- {
- Accessible* focus = FocusMgr()->FocusedAccessible();
- if (focus && (focus == this || focus->Parent() == this))
- return focus;
- return nullptr;
- }
- Accessible*
- Accessible::ChildAtPoint(int32_t aX, int32_t aY,
- EWhichChildAtPoint aWhichChild)
- {
- // If we can't find the point in a child, we will return the fallback answer:
- // we return |this| if the point is within it, otherwise nullptr.
- Accessible* fallbackAnswer = nullptr;
- nsIntRect rect = Bounds();
- if (aX >= rect.x && aX < rect.x + rect.width &&
- aY >= rect.y && aY < rect.y + rect.height)
- fallbackAnswer = this;
- if (nsAccUtils::MustPrune(this)) // Do not dig any further
- return fallbackAnswer;
- // Search an accessible at the given point starting from accessible document
- // because containing block (see CSS2) for out of flow element (for example,
- // absolutely positioned element) may be different from its DOM parent and
- // therefore accessible for containing block may be different from accessible
- // for DOM parent but GetFrameForPoint() should be called for containing block
- // to get an out of flow element.
- DocAccessible* accDocument = Document();
- NS_ENSURE_TRUE(accDocument, nullptr);
- nsIFrame* rootFrame = accDocument->GetFrame();
- NS_ENSURE_TRUE(rootFrame, nullptr);
- nsIFrame* startFrame = rootFrame;
- // Check whether the point is at popup content.
- nsIWidget* rootWidget = rootFrame->GetView()->GetNearestWidget(nullptr);
- NS_ENSURE_TRUE(rootWidget, nullptr);
- LayoutDeviceIntRect rootRect = rootWidget->GetScreenBounds();
- WidgetMouseEvent dummyEvent(true, eMouseMove, rootWidget,
- WidgetMouseEvent::eSynthesized);
- dummyEvent.mRefPoint = LayoutDeviceIntPoint(aX - rootRect.x, aY - rootRect.y);
- nsIFrame* popupFrame = nsLayoutUtils::
- GetPopupFrameForEventCoordinates(accDocument->PresContext()->GetRootPresContext(),
- &dummyEvent);
- if (popupFrame) {
- // If 'this' accessible is not inside the popup then ignore the popup when
- // searching an accessible at point.
- DocAccessible* popupDoc =
- GetAccService()->GetDocAccessible(popupFrame->GetContent()->OwnerDoc());
- Accessible* popupAcc =
- popupDoc->GetAccessibleOrContainer(popupFrame->GetContent());
- Accessible* popupChild = this;
- while (popupChild && !popupChild->IsDoc() && popupChild != popupAcc)
- popupChild = popupChild->Parent();
- if (popupChild == popupAcc)
- startFrame = popupFrame;
- }
- nsPresContext* presContext = startFrame->PresContext();
- nsRect screenRect = startFrame->GetScreenRectInAppUnits();
- nsPoint offset(presContext->DevPixelsToAppUnits(aX) - screenRect.x,
- presContext->DevPixelsToAppUnits(aY) - screenRect.y);
- nsIFrame* foundFrame = nsLayoutUtils::GetFrameForPoint(startFrame, offset);
- nsIContent* content = nullptr;
- if (!foundFrame || !(content = foundFrame->GetContent()))
- return fallbackAnswer;
- // Get accessible for the node with the point or the first accessible in
- // the DOM parent chain.
- DocAccessible* contentDocAcc = GetAccService()->
- GetDocAccessible(content->OwnerDoc());
- // contentDocAcc in some circumstances can be nullptr. See bug 729861
- NS_ASSERTION(contentDocAcc, "could not get the document accessible");
- if (!contentDocAcc)
- return fallbackAnswer;
- Accessible* accessible = contentDocAcc->GetAccessibleOrContainer(content);
- if (!accessible)
- return fallbackAnswer;
- // Hurray! We have an accessible for the frame that layout gave us.
- // Since DOM node of obtained accessible may be out of flow then we should
- // ensure obtained accessible is a child of this accessible.
- Accessible* child = accessible;
- while (child != this) {
- Accessible* parent = child->Parent();
- if (!parent) {
- // Reached the top of the hierarchy. These bounds were inside an
- // accessible that is not a descendant of this one.
- return fallbackAnswer;
- }
- // If we landed on a legitimate child of |this|, and we want the direct
- // child, return it here.
- if (parent == this && aWhichChild == eDirectChild)
- return child;
- child = parent;
- }
- // Manually walk through accessible children and see if the are within this
- // point. Skip offscreen or invisible accessibles. This takes care of cases
- // where layout won't walk into things for us, such as image map areas and
- // sub documents (XXX: subdocuments should be handled by methods of
- // OuterDocAccessibles).
- uint32_t childCount = accessible->ChildCount();
- for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
- Accessible* child = accessible->GetChildAt(childIdx);
- nsIntRect childRect = child->Bounds();
- if (aX >= childRect.x && aX < childRect.x + childRect.width &&
- aY >= childRect.y && aY < childRect.y + childRect.height &&
- (child->State() & states::INVISIBLE) == 0) {
- if (aWhichChild == eDeepestChild)
- return child->ChildAtPoint(aX, aY, eDeepestChild);
- return child;
- }
- }
- return accessible;
- }
- nsRect
- Accessible::RelativeBounds(nsIFrame** aBoundingFrame) const
- {
- nsIFrame* frame = GetFrame();
- if (frame && mContent) {
- bool* hasHitRegionRect = static_cast<bool*>(mContent->GetProperty(nsGkAtoms::hitregion));
- if (hasHitRegionRect && mContent->IsElement()) {
- // This is for canvas fallback content
- // Find a canvas frame the found hit region is relative to.
- nsIFrame* canvasFrame = frame->GetParent();
- if (canvasFrame) {
- canvasFrame = nsLayoutUtils::GetClosestFrameOfType(canvasFrame, nsGkAtoms::HTMLCanvasFrame);
- }
- // make the canvas the bounding frame
- if (canvasFrame) {
- *aBoundingFrame = canvasFrame;
- dom::HTMLCanvasElement *canvas =
- dom::HTMLCanvasElement::FromContent(canvasFrame->GetContent());
- // get the bounding rect of the hit region
- nsRect bounds;
- if (canvas && canvas->CountContexts() &&
- canvas->GetContextAtIndex(0)->GetHitRegionRect(mContent->AsElement(), bounds)) {
- return bounds;
- }
- }
- }
- *aBoundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
- return nsLayoutUtils::
- GetAllInFlowRectsUnion(frame, *aBoundingFrame,
- nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
- }
- return nsRect();
- }
- nsIntRect
- Accessible::Bounds() const
- {
- nsIFrame* boundingFrame = nullptr;
- nsRect unionRectTwips = RelativeBounds(&boundingFrame);
- if (!boundingFrame)
- return nsIntRect();
- nsIntRect screenRect;
- nsPresContext* presContext = mDoc->PresContext();
- screenRect.x = presContext->AppUnitsToDevPixels(unionRectTwips.x);
- screenRect.y = presContext->AppUnitsToDevPixels(unionRectTwips.y);
- screenRect.width = presContext->AppUnitsToDevPixels(unionRectTwips.width);
- screenRect.height = presContext->AppUnitsToDevPixels(unionRectTwips.height);
- // We have the union of the rectangle, now we need to put it in absolute
- // screen coords.
- nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
- ToNearestPixels(presContext->AppUnitsPerDevPixel());
- screenRect.x += orgRectPixels.x;
- screenRect.y += orgRectPixels.y;
- return screenRect;
- }
- void
- Accessible::SetSelected(bool aSelect)
- {
- if (!HasOwnContent())
- return;
- Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
- if (select) {
- if (select->State() & states::MULTISELECTABLE) {
- if (ARIARoleMap()) {
- if (aSelect) {
- mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected,
- NS_LITERAL_STRING("true"), true);
- } else {
- mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::aria_selected, true);
- }
- }
- return;
- }
- if (aSelect)
- TakeFocus();
- }
- }
- void
- Accessible::TakeSelection()
- {
- Accessible* select = nsAccUtils::GetSelectableContainer(this, State());
- if (select) {
- if (select->State() & states::MULTISELECTABLE)
- select->UnselectAll();
- SetSelected(true);
- }
- }
- void
- Accessible::TakeFocus()
- {
- nsIFrame* frame = GetFrame();
- if (!frame)
- return;
- nsIContent* focusContent = mContent;
- // If the accessible focus is managed by container widget then focus the
- // widget and set the accessible as its current item.
- if (!frame->IsFocusable()) {
- Accessible* widget = ContainerWidget();
- if (widget && widget->AreItemsOperable()) {
- nsIContent* widgetElm = widget->GetContent();
- nsIFrame* widgetFrame = widgetElm->GetPrimaryFrame();
- if (widgetFrame && widgetFrame->IsFocusable()) {
- focusContent = widgetElm;
- widget->SetCurrentItem(this);
- }
- }
- }
- nsCOMPtr<nsIDOMElement> element(do_QueryInterface(focusContent));
- nsFocusManager* fm = nsFocusManager::GetFocusManager();
- if (fm)
- fm->SetFocus(element, 0);
- }
- void
- Accessible::XULElmName(DocAccessible* aDocument,
- nsIContent* aElm, nsString& aName)
- {
- /**
- * 3 main cases for XUL Controls to be labeled
- * 1 - control contains label="foo"
- * 2 - control has, as a child, a label element
- * - label has either value="foo" or children
- * 3 - non-child label contains control="controlID"
- * - label has either value="foo" or children
- * Once a label is found, the search is discontinued, so a control
- * that has a label child as well as having a label external to
- * the control that uses the control="controlID" syntax will use
- * the child label for its Name.
- */
- // CASE #1 (via label attribute) -- great majority of the cases
- nsCOMPtr<nsIDOMXULLabeledControlElement> labeledEl = do_QueryInterface(aElm);
- if (labeledEl) {
- labeledEl->GetLabel(aName);
- } else {
- nsCOMPtr<nsIDOMXULSelectControlItemElement> itemEl = do_QueryInterface(aElm);
- if (itemEl) {
- itemEl->GetLabel(aName);
- } else {
- nsCOMPtr<nsIDOMXULSelectControlElement> select = do_QueryInterface(aElm);
- // Use label if this is not a select control element which
- // uses label attribute to indicate which option is selected
- if (!select) {
- nsCOMPtr<nsIDOMXULElement> xulEl(do_QueryInterface(aElm));
- if (xulEl)
- xulEl->GetAttribute(NS_LITERAL_STRING("label"), aName);
- }
- }
- }
- // CASES #2 and #3 ------ label as a child or <label control="id" ... > </label>
- if (aName.IsEmpty()) {
- Accessible* labelAcc = nullptr;
- XULLabelIterator iter(aDocument, aElm);
- while ((labelAcc = iter.Next())) {
- nsCOMPtr<nsIDOMXULLabelElement> xulLabel =
- do_QueryInterface(labelAcc->GetContent());
- // Check if label's value attribute is used
- if (xulLabel && NS_SUCCEEDED(xulLabel->GetValue(aName)) && aName.IsEmpty()) {
- // If no value attribute, a non-empty label must contain
- // children that define its text -- possibly using HTML
- nsTextEquivUtils::
- AppendTextEquivFromContent(labelAcc, labelAcc->GetContent(), &aName);
- }
- }
- }
- aName.CompressWhitespace();
- if (!aName.IsEmpty())
- return;
- // Can get text from title of <toolbaritem> if we're a child of a <toolbaritem>
- nsIContent *bindingParent = aElm->GetBindingParent();
- nsIContent* parent =
- bindingParent? bindingParent->GetParent() : aElm->GetParent();
- nsAutoString ancestorTitle;
- while (parent) {
- if (parent->IsXULElement(nsGkAtoms::toolbaritem) &&
- parent->GetAttr(kNameSpaceID_None, nsGkAtoms::title, ancestorTitle)) {
- // Before returning this, check if the element itself has a tooltip:
- if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::tooltiptext, aName)) {
- aName.CompressWhitespace();
- return;
- }
- aName.Assign(ancestorTitle);
- aName.CompressWhitespace();
- return;
- }
- parent = parent->GetParent();
- }
- }
- nsresult
- Accessible::HandleAccEvent(AccEvent* aEvent)
- {
- NS_ENSURE_ARG_POINTER(aEvent);
- if (IPCAccessibilityActive() && Document()) {
- DocAccessibleChild* ipcDoc = mDoc->IPCDoc();
- MOZ_ASSERT(ipcDoc);
- if (ipcDoc) {
- uint64_t id = aEvent->GetAccessible()->IsDoc() ? 0 :
- reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
- switch(aEvent->GetEventType()) {
- case nsIAccessibleEvent::EVENT_SHOW:
- ipcDoc->ShowEvent(downcast_accEvent(aEvent));
- break;
- case nsIAccessibleEvent::EVENT_HIDE:
- ipcDoc->SendHideEvent(id, aEvent->IsFromUserInput());
- break;
- case nsIAccessibleEvent::EVENT_REORDER:
- // reorder events on the application acc aren't necessary to tell the parent
- // about new top level documents.
- if (!aEvent->GetAccessible()->IsApplication())
- ipcDoc->SendEvent(id, aEvent->GetEventType());
- break;
- case nsIAccessibleEvent::EVENT_STATE_CHANGE: {
- AccStateChangeEvent* event = downcast_accEvent(aEvent);
- ipcDoc->SendStateChangeEvent(id, event->GetState(),
- event->IsStateEnabled());
- break;
- }
- case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED: {
- AccCaretMoveEvent* event = downcast_accEvent(aEvent);
- ipcDoc->SendCaretMoveEvent(id, event->GetCaretOffset());
- break;
- }
- case nsIAccessibleEvent::EVENT_TEXT_INSERTED:
- case nsIAccessibleEvent::EVENT_TEXT_REMOVED: {
- AccTextChangeEvent* event = downcast_accEvent(aEvent);
- ipcDoc->SendTextChangeEvent(id, event->ModifiedText(),
- event->GetStartOffset(),
- event->GetLength(),
- event->IsTextInserted(),
- event->IsFromUserInput());
- break;
- }
- case nsIAccessibleEvent::EVENT_SELECTION:
- case nsIAccessibleEvent::EVENT_SELECTION_ADD:
- case nsIAccessibleEvent::EVENT_SELECTION_REMOVE: {
- AccSelChangeEvent* selEvent = downcast_accEvent(aEvent);
- uint64_t widgetID = selEvent->Widget()->IsDoc() ? 0 :
- reinterpret_cast<uintptr_t>(selEvent->Widget());
- ipcDoc->SendSelectionEvent(id, widgetID, aEvent->GetEventType());
- break;
- }
- default:
- ipcDoc->SendEvent(id, aEvent->GetEventType());
- }
- }
- }
- if (nsCoreUtils::AccEventObserversExist()) {
- nsCoreUtils::DispatchAccEvent(MakeXPCEvent(aEvent));
- }
- return NS_OK;
- }
- already_AddRefed<nsIPersistentProperties>
- Accessible::Attributes()
- {
- nsCOMPtr<nsIPersistentProperties> attributes = NativeAttributes();
- if (!HasOwnContent() || !mContent->IsElement())
- return attributes.forget();
- // 'xml-roles' attribute for landmark.
- nsIAtom* landmark = LandmarkRole();
- if (landmark) {
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, landmark);
- } else {
- // 'xml-roles' attribute coming from ARIA.
- nsAutoString xmlRoles;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::role, xmlRoles))
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::xmlroles, xmlRoles);
- }
- // Expose object attributes from ARIA attributes.
- nsAutoString unused;
- aria::AttrIterator attribIter(mContent);
- nsAutoString name, value;
- while(attribIter.Next(name, value))
- attributes->SetStringProperty(NS_ConvertUTF16toUTF8(name), value, unused);
- if (IsARIAHidden()) {
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::hidden,
- NS_LITERAL_STRING("true"));
- }
- // If there is no aria-live attribute then expose default value of 'live'
- // object attribute used for ARIA role of this accessible.
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (roleMapEntry) {
- if (roleMapEntry->Is(nsGkAtoms::searchbox)) {
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textInputType,
- NS_LITERAL_STRING("search"));
- }
- nsAutoString live;
- nsAccUtils::GetAccAttr(attributes, nsGkAtoms::live, live);
- if (live.IsEmpty()) {
- if (nsAccUtils::GetLiveAttrValue(roleMapEntry->liveAttRule, live))
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::live, live);
- }
- }
- return attributes.forget();
- }
- already_AddRefed<nsIPersistentProperties>
- Accessible::NativeAttributes()
- {
- nsCOMPtr<nsIPersistentProperties> attributes =
- do_CreateInstance(NS_PERSISTENTPROPERTIES_CONTRACTID);
- nsAutoString unused;
- // We support values, so expose the string value as well, via the valuetext
- // object attribute. We test for the value interface because we don't want
- // to expose traditional Value() information such as URL's on links and
- // documents, or text in an input.
- if (HasNumericValue()) {
- nsAutoString valuetext;
- Value(valuetext);
- attributes->SetStringProperty(NS_LITERAL_CSTRING("valuetext"), valuetext,
- unused);
- }
- // Expose checkable object attribute if the accessible has checkable state
- if (State() & states::CHECKABLE) {
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::checkable,
- NS_LITERAL_STRING("true"));
- }
- // Expose 'explicit-name' attribute.
- nsAutoString name;
- if (Name(name) != eNameFromSubtree && !name.IsVoid()) {
- attributes->SetStringProperty(NS_LITERAL_CSTRING("explicit-name"),
- NS_LITERAL_STRING("true"), unused);
- }
- // Group attributes (level/setsize/posinset)
- GroupPos groupPos = GroupPosition();
- nsAccUtils::SetAccGroupAttrs(attributes, groupPos.level,
- groupPos.setSize, groupPos.posInSet);
- // If the accessible doesn't have own content (such as list item bullet or
- // xul tree item) then don't calculate content based attributes.
- if (!HasOwnContent())
- return attributes.forget();
- nsEventShell::GetEventAttributes(GetNode(), attributes);
- // Get container-foo computed live region properties based on the closest
- // container with the live region attribute. Inner nodes override outer nodes
- // within the same document. The inner nodes can be used to override live
- // region behavior on more general outer nodes. However, nodes in outer
- // documents override nodes in inner documents: outer doc author may want to
- // override properties on a widget they used in an iframe.
- nsIContent* startContent = mContent;
- while (startContent) {
- nsIDocument* doc = startContent->GetComposedDoc();
- if (!doc)
- break;
- nsAccUtils::SetLiveContainerAttributes(attributes, startContent,
- doc->GetRootElement());
- // Allow ARIA live region markup from outer documents to override
- nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
- if (!docShellTreeItem)
- break;
- nsCOMPtr<nsIDocShellTreeItem> sameTypeParent;
- docShellTreeItem->GetSameTypeParent(getter_AddRefs(sameTypeParent));
- if (!sameTypeParent || sameTypeParent == docShellTreeItem)
- break;
- nsIDocument* parentDoc = doc->GetParentDocument();
- if (!parentDoc)
- break;
- startContent = parentDoc->FindContentForSubDocument(doc);
- }
- if (!mContent->IsElement())
- return attributes.forget();
- nsAutoString id;
- if (nsCoreUtils::GetID(mContent, id))
- attributes->SetStringProperty(NS_LITERAL_CSTRING("id"), id, unused);
- // Expose class because it may have useful microformat information.
- nsAutoString _class;
- if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::_class, _class))
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::_class, _class);
- // Expose tag.
- nsAutoString tagName;
- mContent->NodeInfo()->GetName(tagName);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::tag, tagName);
- // Expose draggable object attribute.
- nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mContent);
- if (htmlElement) {
- bool draggable = false;
- htmlElement->GetDraggable(&draggable);
- if (draggable) {
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::draggable,
- NS_LITERAL_STRING("true"));
- }
- }
- // Don't calculate CSS-based object attributes when no frame (i.e.
- // the accessible is unattached from the tree).
- if (!mContent->GetPrimaryFrame())
- return attributes.forget();
- // CSS style based object attributes.
- nsAutoString value;
- StyleInfo styleInfo(mContent->AsElement(), mDoc->PresShell());
- // Expose 'display' attribute.
- styleInfo.Display(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::display, value);
- // Expose 'text-align' attribute.
- styleInfo.TextAlign(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textAlign, value);
- // Expose 'text-indent' attribute.
- styleInfo.TextIndent(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::textIndent, value);
- // Expose 'margin-left' attribute.
- styleInfo.MarginLeft(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginLeft, value);
- // Expose 'margin-right' attribute.
- styleInfo.MarginRight(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginRight, value);
- // Expose 'margin-top' attribute.
- styleInfo.MarginTop(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginTop, value);
- // Expose 'margin-bottom' attribute.
- styleInfo.MarginBottom(value);
- nsAccUtils::SetAccAttr(attributes, nsGkAtoms::marginBottom, value);
- return attributes.forget();
- }
- GroupPos
- Accessible::GroupPosition()
- {
- GroupPos groupPos;
- if (!HasOwnContent())
- return groupPos;
- // Get group position from ARIA attributes.
- nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_level, &groupPos.level);
- nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_setsize, &groupPos.setSize);
- nsCoreUtils::GetUIntAttr(mContent, nsGkAtoms::aria_posinset, &groupPos.posInSet);
- // If ARIA is missed and the accessible is visible then calculate group
- // position from hierarchy.
- if (State() & states::INVISIBLE)
- return groupPos;
- // Calculate group level if ARIA is missed.
- if (groupPos.level == 0) {
- int32_t level = GetLevelInternal();
- if (level != 0)
- groupPos.level = level;
- }
- // Calculate position in group and group size if ARIA is missed.
- if (groupPos.posInSet == 0 || groupPos.setSize == 0) {
- int32_t posInSet = 0, setSize = 0;
- GetPositionAndSizeInternal(&posInSet, &setSize);
- if (posInSet != 0 && setSize != 0) {
- if (groupPos.posInSet == 0)
- groupPos.posInSet = posInSet;
- if (groupPos.setSize == 0)
- groupPos.setSize = setSize;
- }
- }
- return groupPos;
- }
- uint64_t
- Accessible::State()
- {
- if (IsDefunct())
- return states::DEFUNCT;
- uint64_t state = NativeState();
- // Apply ARIA states to be sure accessible states will be overridden.
- ApplyARIAState(&state);
- // If this is an ARIA item of the selectable widget and if it's focused and
- // not marked unselected explicitly (i.e. aria-selected="false") then expose
- // it as selected to make ARIA widget authors life easier.
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (roleMapEntry && !(state & states::SELECTED) &&
- !mContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::aria_selected,
- nsGkAtoms::_false, eCaseMatters)) {
- // Special case for tabs: focused tab or focus inside related tab panel
- // implies selected state.
- if (roleMapEntry->role == roles::PAGETAB) {
- if (state & states::FOCUSED) {
- state |= states::SELECTED;
- } else {
- // If focus is in a child of the tab panel surely the tab is selected!
- Relation rel = RelationByType(RelationType::LABEL_FOR);
- Accessible* relTarget = nullptr;
- while ((relTarget = rel.Next())) {
- if (relTarget->Role() == roles::PROPERTYPAGE &&
- FocusMgr()->IsFocusWithin(relTarget))
- state |= states::SELECTED;
- }
- }
- } else if (state & states::FOCUSED) {
- Accessible* container = nsAccUtils::GetSelectableContainer(this, state);
- if (container &&
- !nsAccUtils::HasDefinedARIAToken(container->GetContent(),
- nsGkAtoms::aria_multiselectable)) {
- state |= states::SELECTED;
- }
- }
- }
- const uint32_t kExpandCollapseStates = states::COLLAPSED | states::EXPANDED;
- if ((state & kExpandCollapseStates) == kExpandCollapseStates) {
- // Cannot be both expanded and collapsed -- this happens in ARIA expanded
- // combobox because of limitation of ARIAMap.
- // XXX: Perhaps we will be able to make this less hacky if we support
- // extended states in ARIAMap, e.g. derive COLLAPSED from
- // EXPANDABLE && !EXPANDED.
- state &= ~states::COLLAPSED;
- }
- if (!(state & states::UNAVAILABLE)) {
- state |= states::ENABLED | states::SENSITIVE;
- // If the object is a current item of container widget then mark it as
- // ACTIVE. This allows screen reader virtual buffer modes to know which
- // descendant is the current one that would get focus if the user navigates
- // to the container widget.
- Accessible* widget = ContainerWidget();
- if (widget && widget->CurrentItem() == this)
- state |= states::ACTIVE;
- }
- if ((state & states::COLLAPSED) || (state & states::EXPANDED))
- state |= states::EXPANDABLE;
- // For some reasons DOM node may have not a frame. We tract such accessibles
- // as invisible.
- nsIFrame *frame = GetFrame();
- if (!frame)
- return state;
- if (frame->StyleEffects()->mOpacity == 1.0f &&
- !(state & states::INVISIBLE)) {
- state |= states::OPAQUE1;
- }
- return state;
- }
- void
- Accessible::ApplyARIAState(uint64_t* aState) const
- {
- if (!mContent->IsElement())
- return;
- dom::Element* element = mContent->AsElement();
- // Test for universal states first
- *aState |= aria::UniversalStatesFor(element);
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (roleMapEntry) {
- // We only force the readonly bit off if we have a real mapping for the aria
- // role. This preserves the ability for screen readers to use readonly
- // (primarily on the document) as the hint for creating a virtual buffer.
- if (roleMapEntry->role != roles::NOTHING)
- *aState &= ~states::READONLY;
- if (mContent->HasID()) {
- // If has a role & ID and aria-activedescendant on the container, assume
- // focusable.
- const Accessible* ancestor = this;
- while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
- dom::Element* el = ancestor->Elm();
- if (el &&
- el->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant)) {
- *aState |= states::FOCUSABLE;
- break;
- }
- }
- }
- }
- if (*aState & states::FOCUSABLE) {
- // Propogate aria-disabled from ancestors down to any focusable descendant.
- const Accessible* ancestor = this;
- while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
- dom::Element* el = ancestor->Elm();
- if (el && el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
- nsGkAtoms::_true, eCaseMatters)) {
- *aState |= states::UNAVAILABLE;
- break;
- }
- }
- }
- // special case: A native button element whose role got transformed by ARIA to a toggle button
- // Also applies to togglable button menus, like in the Dev Tools Web Console.
- if (IsButton() || IsMenuButton())
- aria::MapToState(aria::eARIAPressed, element, aState);
- if (!roleMapEntry)
- return;
- *aState |= roleMapEntry->state;
- if (aria::MapToState(roleMapEntry->attributeMap1, element, aState) &&
- aria::MapToState(roleMapEntry->attributeMap2, element, aState) &&
- aria::MapToState(roleMapEntry->attributeMap3, element, aState))
- aria::MapToState(roleMapEntry->attributeMap4, element, aState);
- // ARIA gridcell inherits editable/readonly states from the grid until it's
- // overridden.
- if ((roleMapEntry->Is(nsGkAtoms::gridcell) ||
- roleMapEntry->Is(nsGkAtoms::columnheader) ||
- roleMapEntry->Is(nsGkAtoms::rowheader)) &&
- !(*aState & (states::READONLY | states::EDITABLE))) {
- const TableCellAccessible* cell = AsTableCell();
- if (cell) {
- TableAccessible* table = cell->Table();
- if (table) {
- Accessible* grid = table->AsAccessible();
- uint64_t gridState = 0;
- grid->ApplyARIAState(&gridState);
- *aState |= (gridState & (states::READONLY | states::EDITABLE));
- }
- }
- }
- }
- void
- Accessible::Value(nsString& aValue)
- {
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (!roleMapEntry)
- return;
- if (roleMapEntry->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 textbox is a textified subtree.
- if (roleMapEntry->Is(nsGkAtoms::textbox)) {
- nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
- return;
- }
- // Value of combobox is a text of current or selected item.
- if (roleMapEntry->Is(nsGkAtoms::combobox)) {
- Accessible* option = CurrentItem();
- if (!option) {
- uint32_t childCount = ChildCount();
- for (uint32_t idx = 0; idx < childCount; idx++) {
- Accessible* child = mChildren.ElementAt(idx);
- if (child->IsListControl()) {
- option = child->GetSelectedItem(0);
- break;
- }
- }
- }
- if (option)
- nsTextEquivUtils::GetTextEquivFromSubtree(option, aValue);
- }
- }
- double
- Accessible::MaxValue() const
- {
- return AttrNumericValue(nsGkAtoms::aria_valuemax);
- }
- double
- Accessible::MinValue() const
- {
- return AttrNumericValue(nsGkAtoms::aria_valuemin);
- }
- double
- Accessible::Step() const
- {
- return UnspecifiedNaN<double>(); // no mimimum increment (step) in ARIA.
- }
- double
- Accessible::CurValue() const
- {
- return AttrNumericValue(nsGkAtoms::aria_valuenow);
- }
- bool
- Accessible::SetCurValue(double aValue)
- {
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
- return false;
- const uint32_t kValueCannotChange = states::READONLY | states::UNAVAILABLE;
- if (State() & kValueCannotChange)
- return false;
- double checkValue = MinValue();
- if (!IsNaN(checkValue) && aValue < checkValue)
- return false;
- checkValue = MaxValue();
- if (!IsNaN(checkValue) && aValue > checkValue)
- return false;
- nsAutoString strValue;
- strValue.AppendFloat(aValue);
- return NS_SUCCEEDED(
- mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow, strValue, true));
- }
- role
- Accessible::ARIATransformRole(role aRole)
- {
- // XXX: these unfortunate exceptions don't fit into the ARIA table. This is
- // where the accessible role depends on both the role and ARIA state.
- if (aRole == roles::PUSHBUTTON) {
- if (nsAccUtils::HasDefinedARIAToken(mContent, nsGkAtoms::aria_pressed)) {
- // For simplicity, any existing pressed attribute except "" or "undefined"
- // indicates a toggle.
- return roles::TOGGLE_BUTTON;
- }
- if (mContent->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::aria_haspopup,
- nsGkAtoms::_true,
- eCaseMatters)) {
- // For button with aria-haspopup="true".
- return roles::BUTTONMENU;
- }
- } else if (aRole == roles::LISTBOX) {
- // A listbox inside of a combobox needs a special role because of ATK
- // mapping to menu.
- if (mParent && mParent->Role() == roles::COMBOBOX) {
- return roles::COMBOBOX_LIST;
- } else {
- // Listbox is owned by a combobox
- Relation rel = RelationByType(RelationType::NODE_CHILD_OF);
- Accessible* targetAcc = nullptr;
- while ((targetAcc = rel.Next()))
- if (targetAcc->Role() == roles::COMBOBOX)
- return roles::COMBOBOX_LIST;
- }
- } else if (aRole == roles::OPTION) {
- if (mParent && mParent->Role() == roles::COMBOBOX_LIST)
- return roles::COMBOBOX_OPTION;
- } else if (aRole == roles::MENUITEM) {
- // Menuitem has a submenu.
- if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_haspopup,
- nsGkAtoms::_true, eCaseMatters)) {
- return roles::PARENT_MENUITEM;
- }
- }
- return aRole;
- }
- nsIAtom*
- Accessible::LandmarkRole() const
- {
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- return roleMapEntry && roleMapEntry->IsOfType(eLandmark) ?
- *(roleMapEntry->roleAtom) : nullptr;
- }
- role
- Accessible::NativeRole()
- {
- return roles::NOTHING;
- }
- uint8_t
- Accessible::ActionCount()
- {
- return GetActionRule() == eNoAction ? 0 : 1;
- }
- void
- Accessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
- {
- aName.Truncate();
- if (aIndex != 0)
- return;
- uint32_t actionRule = GetActionRule();
- switch (actionRule) {
- case eActivateAction:
- aName.AssignLiteral("activate");
- return;
- case eClickAction:
- aName.AssignLiteral("click");
- return;
- case ePressAction:
- aName.AssignLiteral("press");
- return;
- case eCheckUncheckAction:
- {
- uint64_t state = State();
- if (state & states::CHECKED)
- aName.AssignLiteral("uncheck");
- else if (state & states::MIXED)
- aName.AssignLiteral("cycle");
- else
- aName.AssignLiteral("check");
- return;
- }
- case eJumpAction:
- aName.AssignLiteral("jump");
- return;
- case eOpenCloseAction:
- if (State() & states::COLLAPSED)
- aName.AssignLiteral("open");
- else
- aName.AssignLiteral("close");
- return;
- case eSelectAction:
- aName.AssignLiteral("select");
- return;
- case eSwitchAction:
- aName.AssignLiteral("switch");
- return;
- case eSortAction:
- aName.AssignLiteral("sort");
- return;
- case eExpandAction:
- if (State() & states::COLLAPSED)
- aName.AssignLiteral("expand");
- else
- aName.AssignLiteral("collapse");
- return;
- }
- }
- bool
- Accessible::DoAction(uint8_t aIndex)
- {
- if (aIndex != 0)
- return false;
- if (GetActionRule() != eNoAction) {
- DoCommand();
- return true;
- }
- return false;
- }
- nsIContent*
- Accessible::GetAtomicRegion() const
- {
- nsIContent *loopContent = mContent;
- nsAutoString atomic;
- while (loopContent && !loopContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_atomic, atomic))
- loopContent = loopContent->GetParent();
- return atomic.EqualsLiteral("true") ? loopContent : nullptr;
- }
- Relation
- Accessible::RelationByType(RelationType aType)
- {
- if (!HasOwnContent())
- return Relation();
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- // Relationships are defined on the same content node that the role would be
- // defined on.
- switch (aType) {
- case RelationType::LABELLED_BY: {
- Relation rel(new IDRefsIterator(mDoc, mContent,
- nsGkAtoms::aria_labelledby));
- if (mContent->IsHTMLElement()) {
- rel.AppendIter(new HTMLLabelIterator(Document(), this));
- } else if (mContent->IsXULElement()) {
- rel.AppendIter(new XULLabelIterator(Document(), mContent));
- }
- return rel;
- }
- case RelationType::LABEL_FOR: {
- Relation rel(new RelatedAccIterator(Document(), mContent,
- nsGkAtoms::aria_labelledby));
- if (mContent->IsXULElement(nsGkAtoms::label))
- rel.AppendIter(new IDRefsIterator(mDoc, mContent, nsGkAtoms::control));
- return rel;
- }
- case RelationType::DESCRIBED_BY: {
- Relation rel(new IDRefsIterator(mDoc, mContent,
- nsGkAtoms::aria_describedby));
- if (mContent->IsXULElement())
- rel.AppendIter(new XULDescriptionIterator(Document(), mContent));
- return rel;
- }
- case RelationType::DESCRIPTION_FOR: {
- Relation rel(new RelatedAccIterator(Document(), mContent,
- nsGkAtoms::aria_describedby));
- // This affectively adds an optional control attribute to xul:description,
- // which only affects accessibility, by allowing the description to be
- // tied to a control.
- if (mContent->IsXULElement(nsGkAtoms::description))
- rel.AppendIter(new IDRefsIterator(mDoc, mContent,
- nsGkAtoms::control));
- return rel;
- }
- case RelationType::NODE_CHILD_OF: {
- Relation rel;
- // This is an ARIA tree or treegrid that doesn't use owns, so we need to
- // get the parent the hard way.
- if (roleMapEntry && (roleMapEntry->role == roles::OUTLINEITEM ||
- roleMapEntry->role == roles::LISTITEM ||
- roleMapEntry->role == roles::ROW)) {
- rel.AppendTarget(GetGroupInfo()->ConceptualParent());
- }
- // If accessible is in its own Window, or is the root of a document,
- // then we should provide NODE_CHILD_OF relation so that MSAA clients
- // can easily get to true parent instead of getting to oleacc's
- // ROLE_WINDOW accessible which will prevent us from going up further
- // (because it is system generated and has no idea about the hierarchy
- // above it).
- nsIFrame *frame = GetFrame();
- if (frame) {
- nsView *view = frame->GetView();
- if (view) {
- nsIScrollableFrame *scrollFrame = do_QueryFrame(frame);
- if (scrollFrame || view->GetWidget() || !frame->GetParent())
- rel.AppendTarget(Parent());
- }
- }
- return rel;
- }
- case RelationType::NODE_PARENT_OF: {
- // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
- // also can be organized by groups.
- if (roleMapEntry &&
- (roleMapEntry->role == roles::OUTLINEITEM ||
- roleMapEntry->role == roles::LISTITEM ||
- roleMapEntry->role == roles::ROW ||
- roleMapEntry->role == roles::OUTLINE ||
- roleMapEntry->role == roles::LIST ||
- roleMapEntry->role == roles::TREE_TABLE)) {
- return Relation(new ItemIterator(this));
- }
- return Relation();
- }
- case RelationType::CONTROLLED_BY:
- return Relation(new RelatedAccIterator(Document(), mContent,
- nsGkAtoms::aria_controls));
- case RelationType::CONTROLLER_FOR: {
- Relation rel(new IDRefsIterator(mDoc, mContent,
- nsGkAtoms::aria_controls));
- rel.AppendIter(new HTMLOutputIterator(Document(), mContent));
- return rel;
- }
- case RelationType::FLOWS_TO:
- return Relation(new IDRefsIterator(mDoc, mContent,
- nsGkAtoms::aria_flowto));
- case RelationType::FLOWS_FROM:
- return Relation(new RelatedAccIterator(Document(), mContent,
- nsGkAtoms::aria_flowto));
- case RelationType::MEMBER_OF:
- return Relation(mDoc, GetAtomicRegion());
- case RelationType::SUBWINDOW_OF:
- case RelationType::EMBEDS:
- case RelationType::EMBEDDED_BY:
- case RelationType::POPUP_FOR:
- case RelationType::PARENT_WINDOW_OF:
- return Relation();
- case RelationType::DEFAULT_BUTTON: {
- if (mContent->IsHTMLElement()) {
- // HTML form controls implements nsIFormControl interface.
- nsCOMPtr<nsIFormControl> control(do_QueryInterface(mContent));
- if (control) {
- nsCOMPtr<nsIForm> form(do_QueryInterface(control->GetFormElement()));
- if (form) {
- nsCOMPtr<nsIContent> formContent =
- do_QueryInterface(form->GetDefaultSubmitElement());
- return Relation(mDoc, formContent);
- }
- }
- } else {
- // In XUL, use first <button default="true" .../> in the document
- nsCOMPtr<nsIDOMXULDocument> xulDoc =
- do_QueryInterface(mContent->OwnerDoc());
- nsCOMPtr<nsIDOMXULButtonElement> buttonEl;
- if (xulDoc) {
- nsCOMPtr<nsIDOMNodeList> possibleDefaultButtons;
- xulDoc->GetElementsByAttribute(NS_LITERAL_STRING("default"),
- NS_LITERAL_STRING("true"),
- getter_AddRefs(possibleDefaultButtons));
- if (possibleDefaultButtons) {
- uint32_t length;
- possibleDefaultButtons->GetLength(&length);
- nsCOMPtr<nsIDOMNode> possibleButton;
- // Check for button in list of default="true" elements
- for (uint32_t count = 0; count < length && !buttonEl; count ++) {
- possibleDefaultButtons->Item(count, getter_AddRefs(possibleButton));
- buttonEl = do_QueryInterface(possibleButton);
- }
- }
- if (!buttonEl) { // Check for anonymous accept button in <dialog>
- dom::Element* rootElm = mContent->OwnerDoc()->GetRootElement();
- if (rootElm) {
- nsIContent* possibleButtonEl = rootElm->OwnerDoc()->
- GetAnonymousElementByAttribute(rootElm, nsGkAtoms::_default,
- NS_LITERAL_STRING("true"));
- buttonEl = do_QueryInterface(possibleButtonEl);
- }
- }
- nsCOMPtr<nsIContent> relatedContent(do_QueryInterface(buttonEl));
- return Relation(mDoc, relatedContent);
- }
- }
- return Relation();
- }
- case RelationType::CONTAINING_DOCUMENT:
- return Relation(mDoc);
- case RelationType::CONTAINING_TAB_PANE: {
- nsCOMPtr<nsIDocShell> docShell =
- nsCoreUtils::GetDocShellFor(GetNode());
- if (docShell) {
- // Walk up the parent chain without crossing the boundary at which item
- // types change, preventing us from walking up out of tab content.
- nsCOMPtr<nsIDocShellTreeItem> root;
- docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
- if (root) {
- // If the item type is typeContent, we assume we are in browser tab
- // content. Note, this includes content such as about:addons,
- // for consistency.
- if (root->ItemType() == nsIDocShellTreeItem::typeContent) {
- return Relation(nsAccUtils::GetDocAccessibleFor(root));
- }
- }
- }
- return Relation();
- }
- case RelationType::CONTAINING_APPLICATION:
- return Relation(ApplicationAcc());
- case RelationType::DETAILS:
- return Relation(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_details));
- case RelationType::DETAILS_FOR:
- return Relation(new RelatedAccIterator(mDoc, mContent, nsGkAtoms::aria_details));
- case RelationType::ERRORMSG:
- return Relation(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_errormessage));
- case RelationType::ERRORMSG_FOR:
- return Relation(new RelatedAccIterator(mDoc, mContent, nsGkAtoms::aria_errormessage));
- default:
- return Relation();
- }
- }
- void
- Accessible::GetNativeInterface(void** aNativeAccessible)
- {
- }
- void
- Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
- {
- class Runnable final : public mozilla::Runnable
- {
- public:
- Runnable(Accessible* aAcc, nsIContent* aContent, uint32_t aIdx) :
- mAcc(aAcc), mContent(aContent), mIdx(aIdx) { }
- NS_IMETHOD Run() override
- {
- if (mAcc)
- mAcc->DispatchClickEvent(mContent, mIdx);
- return NS_OK;
- }
- void Revoke()
- {
- mAcc = nullptr;
- mContent = nullptr;
- }
- private:
- RefPtr<Accessible> mAcc;
- nsCOMPtr<nsIContent> mContent;
- uint32_t mIdx;
- };
- nsIContent* content = aContent ? aContent : mContent.get();
- nsCOMPtr<nsIRunnable> runnable = new Runnable(this, content, aActionIndex);
- NS_DispatchToMainThread(runnable);
- }
- void
- Accessible::DispatchClickEvent(nsIContent *aContent, uint32_t aActionIndex)
- {
- if (IsDefunct())
- return;
- nsCOMPtr<nsIPresShell> presShell = mDoc->PresShell();
- // Scroll into view.
- presShell->ScrollContentIntoView(aContent,
- nsIPresShell::ScrollAxis(),
- nsIPresShell::ScrollAxis(),
- nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
- nsWeakFrame frame = aContent->GetPrimaryFrame();
- if (!frame)
- return;
- // Compute x and y coordinates.
- nsPoint point;
- nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget(point);
- if (!widget)
- return;
- nsSize size = frame->GetSize();
- RefPtr<nsPresContext> presContext = presShell->GetPresContext();
- int32_t x = presContext->AppUnitsToDevPixels(point.x + size.width / 2);
- int32_t y = presContext->AppUnitsToDevPixels(point.y + size.height / 2);
- // Simulate a touch interaction by dispatching touch events with mouse events.
- nsCoreUtils::DispatchTouchEvent(eTouchStart, x, y, aContent, frame,
- presShell, widget);
- nsCoreUtils::DispatchMouseEvent(eMouseDown, x, y, aContent, frame,
- presShell, widget);
- nsCoreUtils::DispatchTouchEvent(eTouchEnd, x, y, aContent, frame,
- presShell, widget);
- nsCoreUtils::DispatchMouseEvent(eMouseUp, x, y, aContent, frame,
- presShell, widget);
- }
- void
- Accessible::ScrollToPoint(uint32_t aCoordinateType, int32_t aX, int32_t aY)
- {
- nsIFrame* frame = GetFrame();
- if (!frame)
- return;
- nsIntPoint coords =
- nsAccUtils::ConvertToScreenCoords(aX, aY, aCoordinateType, this);
- nsIFrame* parentFrame = frame;
- while ((parentFrame = parentFrame->GetParent()))
- nsCoreUtils::ScrollFrameToPoint(parentFrame, frame, coords);
- }
- void
- Accessible::AppendTextTo(nsAString& aText, uint32_t aStartOffset,
- uint32_t aLength)
- {
- // Return text representation of non-text accessible within hypertext
- // accessible. Text accessible overrides this method to return enclosed text.
- if (aStartOffset != 0 || aLength == 0)
- return;
- nsIFrame *frame = GetFrame();
- if (!frame)
- return;
- NS_ASSERTION(mParent,
- "Called on accessible unbound from tree. Result can be wrong.");
- if (frame->GetType() == nsGkAtoms::brFrame) {
- aText += kForcedNewLineChar;
- } else if (mParent && nsAccUtils::MustPrune(mParent)) {
- // Expose the embedded object accessible as imaginary embedded object
- // character if its parent hypertext accessible doesn't expose children to
- // AT.
- aText += kImaginaryEmbeddedObjectChar;
- } else {
- aText += kEmbeddedObjectChar;
- }
- }
- void
- Accessible::Shutdown()
- {
- // Mark the accessible as defunct, invalidate the child count and pointers to
- // other accessibles, also make sure none of its children point to this parent
- mStateFlags |= eIsDefunct;
- int32_t childCount = mChildren.Length();
- for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
- mChildren.ElementAt(childIdx)->UnbindFromParent();
- }
- mChildren.Clear();
- mEmbeddedObjCollector = nullptr;
- if (mParent)
- mParent->RemoveChild(this);
- mContent = nullptr;
- mDoc = nullptr;
- if (SelectionMgr() && SelectionMgr()->AccessibleWithCaret(nullptr) == this)
- SelectionMgr()->ResetCaretOffset();
- }
- // Accessible protected
- void
- Accessible::ARIAName(nsString& aName)
- {
- // aria-labelledby now takes precedence over aria-label
- nsresult rv = nsTextEquivUtils::
- GetTextEquivFromIDRefs(this, nsGkAtoms::aria_labelledby, aName);
- if (NS_SUCCEEDED(rv)) {
- aName.CompressWhitespace();
- }
- if (aName.IsEmpty() &&
- mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_label, aName)) {
- aName.CompressWhitespace();
- }
- }
- // Accessible protected
- ENameValueFlag
- Accessible::NativeName(nsString& aName)
- {
- if (mContent->IsHTMLElement()) {
- Accessible* label = nullptr;
- HTMLLabelIterator iter(Document(), this);
- while ((label = iter.Next())) {
- nsTextEquivUtils::AppendTextEquivFromContent(this, label->GetContent(),
- &aName);
- aName.CompressWhitespace();
- }
- if (!aName.IsEmpty())
- return eNameOK;
- nsTextEquivUtils::GetNameFromSubtree(this, aName);
- return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
- }
- if (mContent->IsXULElement()) {
- XULElmName(mDoc, mContent, aName);
- if (!aName.IsEmpty())
- return eNameOK;
- nsTextEquivUtils::GetNameFromSubtree(this, aName);
- return aName.IsEmpty() ? eNameOK : eNameFromSubtree;
- }
- if (mContent->IsSVGElement()) {
- // 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->IsSVGElement(nsGkAtoms::title)) {
- nsTextEquivUtils::AppendTextEquivFromContent(this, childElm, &aName);
- return eNameOK;
- }
- }
- }
- return eNameOK;
- }
- // Accessible protected
- void
- Accessible::NativeDescription(nsString& aDescription)
- {
- bool isXUL = mContent->IsXULElement();
- if (isXUL) {
- // Try XUL <description control="[id]">description text</description>
- XULDescriptionIterator iter(Document(), mContent);
- Accessible* descr = nullptr;
- while ((descr = iter.Next())) {
- nsTextEquivUtils::AppendTextEquivFromContent(this, descr->GetContent(),
- &aDescription);
- }
- }
- }
- // Accessible protected
- void
- Accessible::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
- {
- MOZ_ASSERT(aParent, "This method isn't used to set null parent");
- MOZ_ASSERT(!mParent, "The child was expected to be moved");
- #ifdef A11Y_LOG
- if (mParent) {
- logging::TreeInfo("BindToParent: stealing accessible", 0,
- "old parent", mParent,
- "new parent", aParent,
- "child", this, nullptr);
- }
- #endif
- mParent = aParent;
- mIndexInParent = aIndexInParent;
- // Note: this is currently only used for richlistitems and their children.
- if (mParent->HasNameDependentParent() || mParent->IsXULListItem())
- mContextFlags |= eHasNameDependentParent;
- else
- mContextFlags &= ~eHasNameDependentParent;
- if (mParent->IsARIAHidden() || aria::HasDefinedARIAHidden(mContent))
- SetARIAHidden(true);
- mContextFlags |=
- static_cast<uint32_t>((mParent->IsAlert() ||
- mParent->IsInsideAlert())) & eInsideAlert;
- }
- // Accessible protected
- void
- Accessible::UnbindFromParent()
- {
- mParent = nullptr;
- mIndexInParent = -1;
- mInt.mIndexOfEmbeddedChild = -1;
- if (IsProxy())
- MOZ_CRASH("this should never be called on proxy wrappers");
- delete mBits.groupInfo;
- mBits.groupInfo = nullptr;
- mContextFlags &= ~eHasNameDependentParent & ~eInsideAlert;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Accessible public methods
- RootAccessible*
- Accessible::RootAccessible() const
- {
- nsCOMPtr<nsIDocShell> docShell = nsCoreUtils::GetDocShellFor(GetNode());
- NS_ASSERTION(docShell, "No docshell for mContent");
- if (!docShell) {
- return nullptr;
- }
- nsCOMPtr<nsIDocShellTreeItem> root;
- docShell->GetRootTreeItem(getter_AddRefs(root));
- NS_ASSERTION(root, "No root content tree item");
- if (!root) {
- return nullptr;
- }
- DocAccessible* docAcc = nsAccUtils::GetDocAccessibleFor(root);
- return docAcc ? docAcc->AsRoot() : nullptr;
- }
- nsIFrame*
- Accessible::GetFrame() const
- {
- return mContent ? mContent->GetPrimaryFrame() : nullptr;
- }
- nsINode*
- Accessible::GetNode() const
- {
- return mContent;
- }
- void
- Accessible::Language(nsAString& aLanguage)
- {
- aLanguage.Truncate();
- if (!mDoc)
- return;
- nsCoreUtils::GetLanguageFor(mContent, nullptr, aLanguage);
- if (aLanguage.IsEmpty()) { // Nothing found, so use document's language
- mDoc->DocumentNode()->GetHeaderData(nsGkAtoms::headerContentLanguage,
- aLanguage);
- }
- }
- bool
- Accessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
- {
- if (!aChild)
- return false;
- if (aIndex == mChildren.Length()) {
- if (!mChildren.AppendElement(aChild))
- return false;
- } else {
- if (!mChildren.InsertElementAt(aIndex, aChild))
- return false;
- MOZ_ASSERT(mStateFlags & eKidsMutating, "Illicit children change");
- for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
- mChildren[idx]->mIndexInParent = idx;
- }
- }
- if (aChild->IsText()) {
- mStateFlags |= eHasTextKids;
- }
- aChild->BindToParent(this, aIndex);
- return true;
- }
- bool
- Accessible::RemoveChild(Accessible* aChild)
- {
- if (!aChild)
- return false;
- if (aChild->mParent != this || aChild->mIndexInParent == -1)
- return false;
- MOZ_ASSERT((mStateFlags & eKidsMutating) || aChild->IsDefunct() || aChild->IsDoc(),
- "Illicit children change");
- int32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
- if (mChildren.SafeElementAt(index) != aChild) {
- MOZ_ASSERT_UNREACHABLE("A wrong child index");
- index = mChildren.IndexOf(aChild);
- if (index == -1) {
- MOZ_ASSERT_UNREACHABLE("No child was found");
- return false;
- }
- }
- aChild->UnbindFromParent();
- mChildren.RemoveElementAt(index);
- for (uint32_t idx = index; idx < mChildren.Length(); idx++) {
- mChildren[idx]->mIndexInParent = idx;
- }
- return true;
- }
- void
- Accessible::MoveChild(uint32_t aNewIndex, Accessible* aChild)
- {
- MOZ_ASSERT(aChild, "No child was given");
- MOZ_ASSERT(aChild->mParent == this, "A child from different subtree was given");
- MOZ_ASSERT(aChild->mIndexInParent != -1, "Unbound child was given");
- MOZ_ASSERT(static_cast<uint32_t>(aChild->mIndexInParent) != aNewIndex,
- "No move, same index");
- MOZ_ASSERT(aNewIndex <= mChildren.Length(), "Wrong new index was given");
- RefPtr<AccHideEvent> hideEvent = new AccHideEvent(aChild, false);
- if (mDoc->Controller()->QueueMutationEvent(hideEvent)) {
- aChild->SetHideEventTarget(true);
- }
- mEmbeddedObjCollector = nullptr;
- mChildren.RemoveElementAt(aChild->mIndexInParent);
- uint32_t startIdx = aNewIndex, endIdx = aChild->mIndexInParent;
- // If the child is moved after its current position.
- if (static_cast<uint32_t>(aChild->mIndexInParent) < aNewIndex) {
- startIdx = aChild->mIndexInParent;
- if (aNewIndex == mChildren.Length() + 1) {
- // The child is moved to the end.
- mChildren.AppendElement(aChild);
- endIdx = mChildren.Length() - 1;
- }
- else {
- mChildren.InsertElementAt(aNewIndex - 1, aChild);
- endIdx = aNewIndex;
- }
- }
- else {
- // The child is moved prior its current position.
- mChildren.InsertElementAt(aNewIndex, aChild);
- }
- for (uint32_t idx = startIdx; idx <= endIdx; idx++) {
- mChildren[idx]->mIndexInParent = idx;
- mChildren[idx]->mStateFlags |= eGroupInfoDirty;
- mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
- }
- RefPtr<AccShowEvent> showEvent = new AccShowEvent(aChild);
- DebugOnly<bool> added = mDoc->Controller()->QueueMutationEvent(showEvent);
- MOZ_ASSERT(added);
- aChild->SetShowEventTarget(true);
- }
- Accessible*
- Accessible::GetChildAt(uint32_t aIndex) const
- {
- Accessible* child = mChildren.SafeElementAt(aIndex, nullptr);
- if (!child)
- return nullptr;
- #ifdef DEBUG
- Accessible* realParent = child->mParent;
- NS_ASSERTION(!realParent || realParent == this,
- "Two accessibles have the same first child accessible!");
- #endif
- return child;
- }
- uint32_t
- Accessible::ChildCount() const
- {
- return mChildren.Length();
- }
- int32_t
- Accessible::IndexInParent() const
- {
- return mIndexInParent;
- }
- uint32_t
- Accessible::EmbeddedChildCount()
- {
- if (mStateFlags & eHasTextKids) {
- if (!mEmbeddedObjCollector)
- mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
- return mEmbeddedObjCollector->Count();
- }
- return ChildCount();
- }
- Accessible*
- Accessible::GetEmbeddedChildAt(uint32_t aIndex)
- {
- if (mStateFlags & eHasTextKids) {
- if (!mEmbeddedObjCollector)
- mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
- return mEmbeddedObjCollector.get() ?
- mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
- }
- return GetChildAt(aIndex);
- }
- int32_t
- Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
- {
- if (mStateFlags & eHasTextKids) {
- if (!mEmbeddedObjCollector)
- mEmbeddedObjCollector.reset(new EmbeddedObjCollector(this));
- return mEmbeddedObjCollector.get() ?
- mEmbeddedObjCollector->GetIndexAt(aChild) : -1;
- }
- return GetIndexOf(aChild);
- }
- ////////////////////////////////////////////////////////////////////////////////
- // HyperLinkAccessible methods
- bool
- Accessible::IsLink()
- {
- // Every embedded accessible within hypertext accessible implements
- // hyperlink interface.
- return mParent && mParent->IsHyperText() && !IsText();
- }
- uint32_t
- Accessible::StartOffset()
- {
- NS_PRECONDITION(IsLink(), "StartOffset is called not on hyper link!");
- HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
- return hyperText ? hyperText->GetChildOffset(this) : 0;
- }
- uint32_t
- Accessible::EndOffset()
- {
- NS_PRECONDITION(IsLink(), "EndOffset is called on not hyper link!");
- HyperTextAccessible* hyperText = mParent ? mParent->AsHyperText() : nullptr;
- return hyperText ? (hyperText->GetChildOffset(this) + 1) : 0;
- }
- uint32_t
- Accessible::AnchorCount()
- {
- NS_PRECONDITION(IsLink(), "AnchorCount is called on not hyper link!");
- return 1;
- }
- Accessible*
- Accessible::AnchorAt(uint32_t aAnchorIndex)
- {
- NS_PRECONDITION(IsLink(), "GetAnchor is called on not hyper link!");
- return aAnchorIndex == 0 ? this : nullptr;
- }
- already_AddRefed<nsIURI>
- Accessible::AnchorURIAt(uint32_t aAnchorIndex)
- {
- NS_PRECONDITION(IsLink(), "AnchorURIAt is called on not hyper link!");
- return nullptr;
- }
- void
- Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
- bool aIsBefore) const
- {
- if (IsHyperText()) {
- *aContainer = const_cast<Accessible*>(this)->AsHyperText();
- *aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount();
- return;
- }
- const Accessible* child = nullptr;
- const Accessible* parent = this;
- do {
- child = parent;
- parent = parent->Parent();
- } while (parent && !parent->IsHyperText());
- if (parent) {
- *aContainer = const_cast<Accessible*>(parent)->AsHyperText();
- *aOffset = (*aContainer)->GetChildOffset(
- child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // SelectAccessible
- void
- Accessible::SelectedItems(nsTArray<Accessible*>* aItems)
- {
- AccIterator iter(this, filters::GetSelected);
- Accessible* selected = nullptr;
- while ((selected = iter.Next()))
- aItems->AppendElement(selected);
- }
- uint32_t
- Accessible::SelectedItemCount()
- {
- uint32_t count = 0;
- AccIterator iter(this, filters::GetSelected);
- Accessible* selected = nullptr;
- while ((selected = iter.Next()))
- ++count;
- return count;
- }
- Accessible*
- Accessible::GetSelectedItem(uint32_t aIndex)
- {
- AccIterator iter(this, filters::GetSelected);
- Accessible* selected = nullptr;
- uint32_t index = 0;
- while ((selected = iter.Next()) && index < aIndex)
- index++;
- return selected;
- }
- bool
- Accessible::IsItemSelected(uint32_t aIndex)
- {
- uint32_t index = 0;
- AccIterator iter(this, filters::GetSelectable);
- Accessible* selected = nullptr;
- while ((selected = iter.Next()) && index < aIndex)
- index++;
- return selected &&
- selected->State() & states::SELECTED;
- }
- bool
- Accessible::AddItemToSelection(uint32_t aIndex)
- {
- uint32_t index = 0;
- AccIterator iter(this, filters::GetSelectable);
- Accessible* selected = nullptr;
- while ((selected = iter.Next()) && index < aIndex)
- index++;
- if (selected)
- selected->SetSelected(true);
- return static_cast<bool>(selected);
- }
- bool
- Accessible::RemoveItemFromSelection(uint32_t aIndex)
- {
- uint32_t index = 0;
- AccIterator iter(this, filters::GetSelectable);
- Accessible* selected = nullptr;
- while ((selected = iter.Next()) && index < aIndex)
- index++;
- if (selected)
- selected->SetSelected(false);
- return static_cast<bool>(selected);
- }
- bool
- Accessible::SelectAll()
- {
- bool success = false;
- Accessible* selectable = nullptr;
- AccIterator iter(this, filters::GetSelectable);
- while((selectable = iter.Next())) {
- success = true;
- selectable->SetSelected(true);
- }
- return success;
- }
- bool
- Accessible::UnselectAll()
- {
- bool success = false;
- Accessible* selected = nullptr;
- AccIterator iter(this, filters::GetSelected);
- while ((selected = iter.Next())) {
- success = true;
- selected->SetSelected(false);
- }
- return success;
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Widgets
- bool
- Accessible::IsWidget() const
- {
- return false;
- }
- bool
- Accessible::IsActiveWidget() const
- {
- if (FocusMgr()->HasDOMFocus(mContent))
- return true;
- // If text entry of combobox widget has a focus then the combobox widget is
- // active.
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (roleMapEntry && roleMapEntry->Is(nsGkAtoms::combobox)) {
- uint32_t childCount = ChildCount();
- for (uint32_t idx = 0; idx < childCount; idx++) {
- Accessible* child = mChildren.ElementAt(idx);
- if (child->Role() == roles::ENTRY)
- return FocusMgr()->HasDOMFocus(child->GetContent());
- }
- }
- return false;
- }
- bool
- Accessible::AreItemsOperable() const
- {
- return HasOwnContent() &&
- mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant);
- }
- Accessible*
- Accessible::CurrentItem()
- {
- // Check for aria-activedescendant, which changes which element has focus.
- // For activedescendant, the ARIA spec does not require that the user agent
- // checks whether pointed node is actually a DOM descendant of the element
- // with the aria-activedescendant attribute.
- nsAutoString id;
- if (HasOwnContent() &&
- mContent->GetAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant, id)) {
- nsIDocument* DOMDoc = mContent->OwnerDoc();
- dom::Element* activeDescendantElm = DOMDoc->GetElementById(id);
- if (activeDescendantElm) {
- DocAccessible* document = Document();
- if (document)
- return document->GetAccessible(activeDescendantElm);
- }
- }
- return nullptr;
- }
- void
- Accessible::SetCurrentItem(Accessible* aItem)
- {
- nsIAtom* id = aItem->GetContent()->GetID();
- if (id) {
- nsAutoString idStr;
- id->ToString(idStr);
- mContent->SetAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant, idStr, true);
- }
- }
- Accessible*
- Accessible::ContainerWidget() const
- {
- if (HasARIARole() && mContent->HasID()) {
- for (Accessible* parent = Parent(); parent; parent = parent->Parent()) {
- nsIContent* parentContent = parent->GetContent();
- if (parentContent &&
- parentContent->HasAttr(kNameSpaceID_None,
- nsGkAtoms::aria_activedescendant)) {
- return parent;
- }
- // Don't cross DOM document boundaries.
- if (parent->IsDoc())
- break;
- }
- }
- return nullptr;
- }
- void
- Accessible::SetARIAHidden(bool aIsDefined)
- {
- if (aIsDefined)
- mContextFlags |= eARIAHidden;
- else
- mContextFlags &= ~eARIAHidden;
- uint32_t length = mChildren.Length();
- for (uint32_t i = 0; i < length; i++) {
- mChildren[i]->SetARIAHidden(aIsDefined);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // Accessible protected methods
- void
- Accessible::LastRelease()
- {
- // First cleanup if needed...
- if (mDoc) {
- Shutdown();
- NS_ASSERTION(!mDoc,
- "A Shutdown() impl forgot to call its parent's Shutdown?");
- }
- // ... then die.
- delete this;
- }
- Accessible*
- Accessible::GetSiblingAtOffset(int32_t aOffset, nsresult* aError) const
- {
- if (!mParent || mIndexInParent == -1) {
- if (aError)
- *aError = NS_ERROR_UNEXPECTED;
- return nullptr;
- }
- if (aError &&
- mIndexInParent + aOffset >= static_cast<int32_t>(mParent->ChildCount())) {
- *aError = NS_OK; // fail peacefully
- return nullptr;
- }
- Accessible* child = mParent->GetChildAt(mIndexInParent + aOffset);
- if (aError && !child)
- *aError = NS_ERROR_UNEXPECTED;
- return child;
- }
- double
- Accessible::AttrNumericValue(nsIAtom* aAttr) const
- {
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (!roleMapEntry || roleMapEntry->valueRule == eNoValue)
- return UnspecifiedNaN<double>();
- nsAutoString attrValue;
- if (!mContent->GetAttr(kNameSpaceID_None, aAttr, attrValue))
- return UnspecifiedNaN<double>();
- nsresult error = NS_OK;
- double value = attrValue.ToDouble(&error);
- return NS_FAILED(error) ? UnspecifiedNaN<double>() : value;
- }
- uint32_t
- Accessible::GetActionRule() const
- {
- if (!HasOwnContent() || (InteractiveState() & states::UNAVAILABLE))
- return eNoAction;
- // Return "click" action on elements that have an attached popup menu.
- if (mContent->IsXULElement())
- if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::popup))
- return eClickAction;
- // Has registered 'click' event handler.
- bool isOnclick = nsCoreUtils::HasClickListener(mContent);
- if (isOnclick)
- return eClickAction;
- // Get an action based on ARIA role.
- const nsRoleMapEntry* roleMapEntry = ARIARoleMap();
- if (roleMapEntry &&
- roleMapEntry->actionRule != eNoAction)
- return roleMapEntry->actionRule;
- // Get an action based on ARIA attribute.
- if (nsAccUtils::HasDefinedARIAToken(mContent,
- nsGkAtoms::aria_expanded))
- return eExpandAction;
- return eNoAction;
- }
- AccGroupInfo*
- Accessible::GetGroupInfo()
- {
- if (IsProxy())
- MOZ_CRASH("This should never be called on proxy wrappers");
- if (mBits.groupInfo){
- if (HasDirtyGroupInfo()) {
- mBits.groupInfo->Update();
- mStateFlags &= ~eGroupInfoDirty;
- }
- return mBits.groupInfo;
- }
- mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
- return mBits.groupInfo;
- }
- void
- Accessible::GetPositionAndSizeInternal(int32_t *aPosInSet, int32_t *aSetSize)
- {
- AccGroupInfo* groupInfo = GetGroupInfo();
- if (groupInfo) {
- *aPosInSet = groupInfo->PosInSet();
- *aSetSize = groupInfo->SetSize();
- }
- }
- int32_t
- Accessible::GetLevelInternal()
- {
- int32_t level = nsAccUtils::GetDefaultLevel(this);
- if (!IsBoundToParent())
- return level;
- roles::Role role = Role();
- if (role == roles::OUTLINEITEM) {
- // Always expose 'level' attribute for 'outlineitem' accessible. The number
- // of nested 'grouping' accessibles containing 'outlineitem' accessible is
- // its level.
- level = 1;
- Accessible* parent = this;
- while ((parent = parent->Parent())) {
- roles::Role parentRole = parent->Role();
- if (parentRole == roles::OUTLINE)
- break;
- if (parentRole == roles::GROUPING)
- ++ level;
- }
- } else if (role == roles::LISTITEM) {
- // Expose 'level' attribute on nested lists. We support two hierarchies:
- // a) list -> listitem -> list -> listitem (nested list is a last child
- // of listitem of the parent list);
- // b) list -> listitem -> group -> listitem (nested listitems are contained
- // by group that is a last child of the parent listitem).
- // Calculate 'level' attribute based on number of parent listitems.
- level = 0;
- Accessible* parent = this;
- while ((parent = parent->Parent())) {
- roles::Role parentRole = parent->Role();
- if (parentRole == roles::LISTITEM)
- ++ level;
- else if (parentRole != roles::LIST && parentRole != roles::GROUPING)
- break;
- }
- if (level == 0) {
- // If this listitem is on top of nested lists then expose 'level'
- // attribute.
- parent = Parent();
- uint32_t siblingCount = parent->ChildCount();
- for (uint32_t siblingIdx = 0; siblingIdx < siblingCount; siblingIdx++) {
- Accessible* sibling = parent->GetChildAt(siblingIdx);
- Accessible* siblingChild = sibling->LastChild();
- if (siblingChild) {
- roles::Role lastChildRole = siblingChild->Role();
- if (lastChildRole == roles::LIST || lastChildRole == roles::GROUPING)
- return 1;
- }
- }
- } else {
- ++ level; // level is 1-index based
- }
- }
- return level;
- }
- void
- Accessible::StaticAsserts() const
- {
- static_assert(eLastStateFlag <= (1 << kStateFlagsBits) - 1,
- "Accessible::mStateFlags was oversized by eLastStateFlag!");
- static_assert(eLastAccType <= (1 << kTypeBits) - 1,
- "Accessible::mType was oversized by eLastAccType!");
- static_assert(eLastContextFlag <= (1 << kContextFlagsBits) - 1,
- "Accessible::mContextFlags was oversized by eLastContextFlag!");
- static_assert(eLastAccGenericType <= (1 << kGenericTypesBits) - 1,
- "Accessible::mGenericType was oversized by eLastAccGenericType!");
- }
- ////////////////////////////////////////////////////////////////////////////////
- // KeyBinding class
- // static
- uint32_t
- KeyBinding::AccelModifier()
- {
- switch (WidgetInputEvent::AccelModifier()) {
- case MODIFIER_ALT:
- return kAlt;
- case MODIFIER_CONTROL:
- return kControl;
- case MODIFIER_META:
- return kMeta;
- case MODIFIER_OS:
- return kOS;
- default:
- MOZ_CRASH("Handle the new result of WidgetInputEvent::AccelModifier()");
- return 0;
- }
- }
- void
- KeyBinding::ToPlatformFormat(nsAString& aValue) const
- {
- nsCOMPtr<nsIStringBundle> keyStringBundle;
- nsCOMPtr<nsIStringBundleService> stringBundleService =
- mozilla::services::GetStringBundleService();
- if (stringBundleService)
- stringBundleService->CreateBundle(
- "chrome://global-platform/locale/platformKeys.properties",
- getter_AddRefs(keyStringBundle));
- if (!keyStringBundle)
- return;
- nsAutoString separator;
- keyStringBundle->GetStringFromName(u"MODIFIER_SEPARATOR",
- getter_Copies(separator));
- nsAutoString modifierName;
- if (mModifierMask & kControl) {
- keyStringBundle->GetStringFromName(u"VK_CONTROL",
- getter_Copies(modifierName));
- aValue.Append(modifierName);
- aValue.Append(separator);
- }
- if (mModifierMask & kAlt) {
- keyStringBundle->GetStringFromName(u"VK_ALT",
- getter_Copies(modifierName));
- aValue.Append(modifierName);
- aValue.Append(separator);
- }
- if (mModifierMask & kShift) {
- keyStringBundle->GetStringFromName(u"VK_SHIFT",
- getter_Copies(modifierName));
- aValue.Append(modifierName);
- aValue.Append(separator);
- }
- if (mModifierMask & kMeta) {
- keyStringBundle->GetStringFromName(u"VK_META",
- getter_Copies(modifierName));
- aValue.Append(modifierName);
- aValue.Append(separator);
- }
- aValue.Append(mKey);
- }
- void
- KeyBinding::ToAtkFormat(nsAString& aValue) const
- {
- nsAutoString modifierName;
- if (mModifierMask & kControl)
- aValue.AppendLiteral("<Control>");
- if (mModifierMask & kAlt)
- aValue.AppendLiteral("<Alt>");
- if (mModifierMask & kShift)
- aValue.AppendLiteral("<Shift>");
- if (mModifierMask & kMeta)
- aValue.AppendLiteral("<Meta>");
- aValue.Append(mKey);
- }
|