Files
palemoon27/accessible/generic/Accessible.cpp
T
roytam1 c6e1bfdcf2 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1261578 - Part 1. Correct text color in selection range; r=jfkthame (6895ebe313)
- Bug 1261578 - Part 2. Correct text decoration color; r=jfkthame (348ba5a946)
- Bug 1261578 - Part 3. Correct MathML text color; r=jfkthame (99dfb99af4)
- Bug 1261578 - Part 4. Correct text overflow color; r=jfkthame (96318becf6)
- Bug 1261578 - Part 5. web-platform-test reftest; r=jfkthame (781bba0317)
- Bug 1110460, part 1 - Add operator= to nsCSSValuePair. r=dholbert (7d836b786b)
- Bug 1110460, part 2 - Move AddPositions() further up StyleAnimationValue.cpp and add asserts. r=dholbert (5f00295917)
- Bug 1110460, part 3 - Factor out a AddCSSValuePair helper in StyleAnimationValue.cpp. r=dholbert (9947835334)
- Bug 1110460, part 4 - Fix MOZ_ASSERT bug in nsComputedDOMStyle.cpp. r=dholbert (5403c0c377)
- Bug 1110460, part 5 - Factor out a nsStyleBasicShape::GetShapeTypeName method. r=dholbert (d27623d42f)
- Bug 1110460, part 6 - Make StyleAnimationValue support css::URLValue backed nsCSSValue objects. r=dholbert (dc006d1cae)
- Bug 1110460, part 7 - Factor out code for adding two nsCSSValuePairList lists. r=dholbert (f27f780005)
- Bug 1260655 - Allow StyleAnimationValue::UncomputeValue to produce values whose storage is independent of the passed-in computed value; r=heycam (abcc77ede5)
- Bug 1110460, part 8 - Support CSS animation of clip-path basic shapes. r=dholbert (cfe5bf883b)
- Bug 1238243 - Reftests involving possibly-ignorable hangul choseong filler. r=xidorn Bug 1238243 followup - Mark test 1238243-2.html as random on OS X 10.6, due to dependency on available fonts. Bug 1239564 - Add reftest for mask layer composition. r=roc (bb3bf56d52)
- Bug 1260543 - Treat currentcolor as computed value which is not interpolatable with actual color for text-emphasis-color and -webkit-text-fill-color. r=birtles (7b90f37e2f)
- Bug 1260543 followup - Fix the function name in test_transitions_per_property.html. DONTBUILD (1362d30c6a)
- Bug 1248708 - Part1: parse and compute -webkit-text-stroke property. r=heycam (c77332031d)
- Bug 1248708 - Part2.1: use mfbt/TypedEnumBits.h for DrawMode. r=jwatt (55bfbdf5dd)
- Bug 1266101, part 1 - Remove various bits of dead code related to painting SVG text. r=heycam (c5140a911c)
- Bug 1266101, part 2 - Remove most of the GLYPH_STROKE/GLYPH_STROKE_UNDERNEATH checking code. r=heycam (6d8f17bc19)
- Bug 1248708 - Part2.2: render -webkit-text-stroke property. r=jfkthame (bccc308ddc)
- Bug 1248708 - Part3: add reftests. r=jfkthame (0fc0864749)
- Bug 1248708 followup: Use MOZ_ARRAY_LENGTH instead of ArrayLength for expression used at compile time, to fix MSVC 2013 bustage. (trivial/bustage-fix, no review) (25cecd7585)
- Bug 1266341 - Pass the right kind of enum constants for script codes. r=masayuki (723e784a37)
- Bug 1245866 - A better buffer size check in nsIDNService::IDNA2008ToUnicode, r=jfkthame (683db7d93c)
- Bug 1233610 - IDN service should return NS_ERROR_MALFORMED_URI instead of NS_ERROR_FAILURE r=mcmanus (d70d4425ca)
- Bug 1266391 - Introduce an enum class mozilla::unicode::Script, and use this instead of bare integers to specify script codes for better type checking. r=masayuki (363a23049f)
- fix 1389436 backport (a8f96f6b33)
- Bug 1258228 - create special child accessibles in owner's constructor, part1, r=marcoz (2bbb819f76)
- Bug 1258228 - create special child accessibles in owner's constructor, part2, r=marcoz (5e20a7761a)
- Bug 1258228 - create special child accessibles in owner's constructor, part3, r=marcoz (6574efb1e7)
- Bug 1258228 - create special child accessibles in owner's constructor, part4, r=marcoz (15420c5a12)
- Bug 1188818 - DataTransfer must deal with nsIFile of directories, r=jwatt (715a857dd1)
- Bug 1126295 - Move TestAtoms.cpp to gtest and enable it; r=froydnj (9275ebeb3c)
- Bug 1257128 (part 1) - Remove nsIAtom.equalsUTF8. r=froydnj. (17238a04e6)
- Bug 1257128 (part 2) - Remove nsIAtomService.get{,Permanent}AtomUTF8. r=froydnj. (f4131f1024)
- Bug 1257128 (part 3) - Remove unused createAtom() function in a test. r=tbsaunde. (e082c1bc17)
- Bug 1257128 (part 4) - Remove unused nsIAtomService references in toolkit/. r=froydnj. (5ddae71ae8)
- Bug 1257402 - Remove NS_NewPermanentAtom() and nsIAtomService.getPermanentAtom(). r=froydnj,kmag. (4b9ad7894e)
- Bug 1145395 - Pass Element& to nsHTMLEditor::RemoveElementIfNoStyleOrIdOrClass; r=ehsan (83080cb024)
- Bug 1140105 - Can't query for a specific font face when the selection is collapsed. r=ehsan (b534c04e7f)
- Bug 1145395 - Introduce an overload of nsHTMLEditor::RemoveStyleInside that takes nsIContent&; r=ehsan (c5cb3bfb1d)
- Bug 1170326 - nsHTMLCSSUtils::ParseLength should check if the input is an empty string, r=ehsan (1950f67c9f)
- Bug 1260871 - Remove do_GetAtom() and rename NS_NewAtom() as NS_Atomize(). r=erahm. (984bc596bf)
- Bug 1254128 - Make the Mozilla .lldbinit use the full path for topsrcdir. r=sparky (20856a94b4)
- Bug 1259659 - rename from InternalUIEvent.detail to InternalUIEvent.mDetail. r=masayuki (37165bd487)
- Bug 1249664 - Make stateful frames responsible for their own keys r=dbaron (03a4365aa1)
- Bug 1232338 - part 1, Add better mouse support to InputData MouseInput r=kats (8be234631e)
- Bug 1259655 - part 1 Rename WidgetEventTime::time to WidgetEventTime::mTime r=masayuki (cd679b408e)
- Bug 1235908 - measure total event wait and process time. r=smaug (4364242809)
- Bug 1259655 - part 2 Rename WidgetEventTime::timeStamp to WidgetEventTime::mTimeStamp r=masayuki (94a507904d)
- Bug 1263785 - Kill off the deprecated nsINode::GetCurrentDoc. r=baku (670e175017)
- Bug 1245748 - Remove no-longer-needed code for directly setting up properties in KeyframeEffect(ReadOnly) constructor; r=heycam (166ed7697c)
- Bug 1259285 - Part1 - Move CSS/Web Animations-specific visibility handling. r=birtles (13a4ac2b94)
- Bug 1259285 - Part2 - Add web-platform test for visibility handling. r=birtles (b7cee58cf7)
- Bug 1260655 - Add KeyframeEffectReadOnly::SetFrames; r=heycam (7da27828da)
- Bug 1260655 - Wrap lines in keyframe-effect/constructor.html to 80 chars; r=whitespace-only (32b6412db2)
- Bug 1260655 - Update keyframe-effect/constructor.html to no longer refer to PropertyIndexedKeyframes or Keyframe; r=heycam (dbe0acf873)
- Bug 1260655 - Return the stored Keyframe objects from GetFrames, when available ; r=heycam (5b8cbc1773)
- Bug 1245748 - Add a Move constructor to Keyframe; r=heycam (eeabc9396a)
- Bug 1260655 - Add an assignment operator to Keyframe that takes an rvalue reference; r=heycam (e81f1f6d7c)
- Bug 1260655 - Add methods to CSSAnimationBuilder to build a set of Keyframe objects; r=heycam (93e2a7b3aa)
- Bug 1260655 - Use CSSAnimationBuilder::BuildAnimationFrames to set up CSS animations using Keyframe objects; r=heycam (4df3ad20cd)
- Bug 1260655 - Drop some no-longer-needed code for setting up CSS animations using AnimationProperty objects; r=heycam (e1d8e020aa)
- Bug 1260655 - Add a copy constructor and copy assignment operator to Keyframe; r=heycam (d9a1ca5d24)
- Bug 1249564 - Part 1: Assign the parent object of AnimationEffectTiming(ReadOnly). r=birtles,bz (298bdcf084)
- Bug 1249564 - Part 2: Cycle collect AnimationEffectTimingReadOnly. r=birtles (25d959d057)
- Bug 1263063 - Part 1: Remove unnecessary clamping of TimingParams::mIterationStart, since it's guaranteed to be nonnegative. r=dholbert (35362749d7)
- Bug 1263063 - Part 2: Add an assertion to ensure that iteration count is nonnegative and finite. r=dholbert (7df13df139)
- Bug 1263063 - Part 3: Change logic in KeyFrameEffect to assume that TimingParams' iteration-count has already been validated as nonnegative & finite. r=dholbert (6dd3f0d984)
- Bug 1263063 - Part 4: Move ActiveDuration() into TimingParams. r=dholbert (6760bdeaa7)
- Bug 1263063 - Part 5: Introduce TimingParams::EndTime(). r=dholbert (af9a4ddd95)
- Bug 1260983 - Allow creating animations with a target element not bound to a document; r=heycam (7d71619cfb)
- Bug 1244591 - Part 1: Implement KeyframeEffect.setFrames r=birtles,smaug (e090fa68b6)
- Bug 1244591 - Part 2: Extract useful keyframes tests to a new file r=birtles (780797fe53)
- Bug 1244591 - Part 3: Add web-platform tests for KeyframeEffect.setFrames r=birtles (997d26427b)
- Bug 1259878 - remove unnecessary isSome(). r=birtles (cb21268a0a)
- Bug 1248532 - Part 1: steps-start does not produce correct value at the beginning of the interval. r=birtles (9131923815)
- Bug 1248532 - Part 2: Add tests for step-start. r=birtles (04ae44375d)
- Bug 1248532 - Part 3: add a test for step-end with iterationStart. r=birtles (769827746b)
- Bug 1266257 - Revise timing model calculations to use fraction-based approach; r=hiro (c1de969ef0)
- Bug 1260572 - Replace AnimValuesStyleRule::AddEmptyValue with an overload of AddValue that takes an rvalue reference; r=heycam (08e01a4cdc)
- Bug 1260572 - Use 50% switch behavior if StyleAnimationValue::Interpolate fails; r=heycam (e67754e770)
- Bug 1260976 - Remove the old AnimationProperty-based GetFrames; r=heycam (efc88117e6)
- Bug 1259248. Add an ArrayView class. r=botond (5bb9e22500)
- Bug 1265648 - Remove the global nsTextFrameTextRunCache, as it no longer serves any useful purpose. r=mats (3ffbf42634)
- Bug 1238243 - Don't filter out Hangul jamo fillers as 'ignorable', because the font may require them to provide advance width. r=xidorn (9fc2f82853)
- Bug 1265648 followup - Use an array of UniquePtr<> for mTextRunsToDelete. r=mats (8745a7566e)
- Bug 1263956 - Adopt the new harfbuzz API for char-to-glyph mapping functions. r=jrmuizel (1f061d7b0f)
- Bug 1265568 - Don't require component alpha layers for text with -moz-osx-font-smoothing:grayscale. r=jfkthame,mattwoodrow (f819591273)
- Bug 1199547 - Backout workaround from bug 722676 as it causes issues on at least OSX 10.10 and 10.11. r=mstange (c96fb3a69d)
- Bug 1265953, part 1 - Convert most of the cocoa widget code from nsAutoPtr to UniquePtr. r=mstange (74aa93eaf9)
- Bug 1249915 - Add ability to synthesize native touch events on GTK for mochitests. r=karlt (b734a366fb)
- Bug 1259670 Rename WidgetPluginEvent::retargetToFocusedDocument to WidgetPluginEvent::mRetargetToFocusedDocument r=smaug (24c12b3bb4)
- Bug 1249915 - Fix synthesized touch injection code on Windows to not apply the scale factor twice. r=jimm (b590300aa3)
- Bug 1250505 - Convert SynthesizeNativeTouchPoint and SynthesizeNativeTouchTap to take a LayoutDeviceIntPoint instead of a ScreenPoint. r=njn (8ddd066519)
- Bug 1256731 - patch 1 - Don't apply theme-dpi scaling to metrics of window border elements, because Windows doesn't respect per-monitor dpi scaling when it draws them. r=emk (582676e33e)
- Bug 1247935 - Part 1: double-buffer nsShmImage. r=jrmuizel (2392a1e916)
- Bug 1247935 - Part 2: use shared pixmaps with XShm for nsShmImage. r=jrmuizel (b3dd105fcc)
- Bug 1265953, part 2 - Convert much of the rest of the widget code from nsAutoPtr to UniquePtr. r=mstange (e0c1c49ef8)
- Bug 1265953, part 3 - Convert more widget code from nsAutoPtr to UniquePtr. r=mstange (17e1be57d2)
- Bug 1265953, part 4 - Convert nsMenuX::LoadSubMenu and nsMenuX::AddMenu to UniquePtr. r=mstange (f4ea50b802)
- Bug 1259664 part.1 Rename WidgetWheelEvent::deltaX to WidgetWheelEvent::mDeltaX r=smaug (8c7ef861eb)
- Bug 1259664 part.2 Rename WidgetWheelEvent::deltaY to WidgetWheelEvent::mDeltaY r=smaug (03529452fb)
- Bug 1259664 part.3 Rename WidgetWheelEvent::deltaZ to WidgetWheelEvent::mDeltaZ r=smaug (0d664b2900)
- Bug 1259664 part.4 Rename WidgetWheelEvent::deltaMode to WidgetWheelEvent::mDeltaMode r=smaug (dc8283ee70)
- Bug 1253041 - Don't apply user wheel prefs more than once. r=smaug (2386a9da14)
- Bug 1259664 part.5 Rename WidgetWheelEvent::customizedByUserPrefs to WidgetWheelEvent::mCustomizedByUserPrefs r=smaug (3b16757c1e)
- Bug 1258820 - making root scrollable element not scroll if mouse wheel is used on xul dropdown r=masayuki (58ab750981)
- Bug 1250050 Add a pref to disable supporting mouse wheel of windowless plugins on Windows r=smaug (1560cfcb35)
- Bug 1256162, use last drag target for dragexit event when comparing to a remote browser, r=smaug (2d03394037)
- Bug 1110030 - part1 - add flags:IsSynthesizedByTIP and InputMethodAppState in WidgetKeyboardEvent. r=masayuki (c8f7802ee0)
- Bug 1110030 - part2 - Prevent PostHandleKeyboardEvent if it's handling by IME. r=masayuki (29fab0d4fe)
- Bug 1110030 - part3 - Interface between PresShell and HardwareKeyHandler. r=masayuki, r=smaug (cb152d6b4b)
- Bug 1110030 - part4 - HardwareKeyHandler component. r=masayuki, r=smaug (9e372e8f19)
- Bug 1110030 - part5 - Expose KeyboardEventInit dictionary. r=masayuki, r=smaug (3c38bf2f37)
- Bug 1110030 - part6 - Expose DefaultPreventedBy*. r=masayuki, r=smaug (5c94c067be)
- Bug 1110030 - part7 - Interface between HardwareKeyHandler and Input Method App. r=masayuki, r=smaug (f33b7f5ad5)
- Bug 1244546 part 1 - Apply proper unit conversion for SynthesizeNativeMouseEvent. r=karlt (8596ff8068)
- Bug 1244546 part 2 - Align the center point for pointerlock to meet widget's requirement. r=smaug (c447b8c770)
- Bug 1259664 part.6 Rename WidgetWheelEvent::mayHaveMomentum to WidgetWheelEvent::mMayHaveMomentum r=smaug (ef95784e26)
- Bug 1259664 part.7 Rename WidgetWheelEvent::isMomentum to WidgetWheelEvent::mIsMomentum r=smaug (983c8dc9e3)
- Bug 1259664 part.8 Rename WidgetWheelEvent::scrollType to WidgetWheelEvent::mScrollType r=smaug (c6731aee2d)
- Bug 1259664 part.9 Rename WidgetWheelEvent::lineOrPageDeltaX to WidgetWheelEvent::mLineOrPageDeltaX r=smaug (9c6d3d7f6b)
- Bug 1259664 part.10 Rename WidgetWheelEvent::lineOrPageDeltaY to WidgetWheelEvent::mLineOrPageDeltaY r=smaug (8a0d549eee)
- Bug 1259664 part.11 Rename WidgetWheelEvent::overflowDeltaX to WidgetWheelEvent::mOverflowDeltaX r=smaug (6200509b12)
- Bug 1259664 part.12 Rename WidgetWheelEvent::overflowDeltaY to Widget¦WheelEvent::mOverflowDeltaY r=smaug (3827fc7cef)
- Bug 1259664 part.13 Reorder the definition of members of WidgetWheelEvent r=smaug (ea69305e49)
- Bug 1243628 - Allow for presshell to have been destroyed and disconnected from prescontext during event dispatch. r=dholbert (b98e7ed1a2)
- Bug 1256952, send a dragexit at remote process when leaving the remote frame, r=smaug (1037c6a425)
- Bug 1261818 - don't pass nsAutoCStrings into nsBaseWidget debugging methods; r=karlt (36b2b66795)
- Bug 1250560 - Fix crash in HandleTouchEvent. r=roc (9586890edf)
- Bug 1121468 - Go to NoActionState after receiving release on LongTapState. r=roc (4e05f9ad70)
- Bug 1248847 - Assert AccessibleCaretEventHub mRefCnt > 1 in all its entry points. r=mats (89ced3c3e2)
- Bug 1251915 - Correct AccessibleCaretEventHub coding style by clang-format. r=roc (12d5b507d7)
- Bug 1259668 Rename WidgetTouchEvent::touches to WidgetTouchEvent::mTouches r=smaug (9c53824e17)
- Bug 1259657 Rename WidgetGUIEvent::widget to WidgetGUIEvent::mWidget r=smaug (8707f57a28)
- Bug 1246477 - Fix carets not updated by scroll events in LongTapState. r=roc (ba6f12001c)
- Bug 1259662 part.1 Rename WidgetDragEvent::dataTransfer to WidgetDragEvent::mDataTransfer r=smaug (79ffebe3ae)
- Bug 1259662 part.2 Rename WidgetDragEvent::userCancelled to WidgetDragEvent::mUserCancelled r=smaug (4b7ee565e7)
- Bug 1241008 - Add preference to show or hide selection bars. r=mtseng (aefd4c430d)
- Bug 1242349 - Provide unique AccessibleCaret pref for JS selection changes, r=TYLin (e964cb16cf)
- Bug 1240917 - Do not show caret in empty input on Fennec. r=roc (d73c6bd94d)
- Bug 1121468 - Show carets when long-pressing on selection highlight. r=roc (bf134067de)
- Bug 1246064 - Support long press to show AccessibleCaret on empty input for Fennec. r=roc (90791443cb)
- Bug 1246918 - Fix carets missing after scrolling down in selection mode on Fennec. r=roc (bc0915ad70)
- Bug 1246918 - Handle PresShell gone after FlushLayout(). r=roc (b2f18c9a03)
- Bug 1251915 - Ignore handling eTouchCancel events. r=roc (a04c3ad8eb)
- Bug 1121468 - Use auto* to explicit declare 'self' as a pointer. r=roc (d83a6020e3)
- Bug 1251346 - Fennec should not generate touch events from mouse events. r=kats (d8077748fc)
- Bug 1259656 part.1 Rename WidgetEvent::refPoint to WidgetEvent::mRefPoint r=smaug (fa66825fc9)
- Bug 1259656 part.2 Rename WidgetEvent::lastRefPoint to WidgetEvent::mLastRefPoint r=smaug (c964d62185)
- Bug 1259656 part.3 Rename WidgetEvent::userType to WidgetEvent::mSpecifiedEventType r=smaug (218ae50355)
- Bug 1259656 part.4 Rename WidgetEvent::typeString to WidgetEvent::mSpecifiedEventTypeString r=smaug (0192c890c9)
- Bug 1259656 part.5 Rename WidgetEvent::target to WidgetEvent::mTarget r=smaug (031356f40d)
- Bug 1259656 part.6 Rename WidgetEvent::currentTarget to WidgetEvent::mCurrentTarget r=smaug (451810f6d3)
- Bug 1259656 part.7 Rename WidgetEvent::originalTarget to WidgetEvent::mOriginalTarget r=smaug (643379c9cb)
- Bug 1259673 rename from InternalClipboardEvent.clipboardData to InternalClipboardEvent.mClipboardData. r=masayuki (5ad3c180fe)
- Bug 1259674 Part 1 rename InternalFocusEvent::fromRaise to InternalFocusEvent::mFrameRaise r=masayuki (400f1ba6e9)
- Bug 1259674 Part 2 rename InternalFocusEvent::isRefocus to InternalFocusEvent::mIsRefocus r=masayuki (93b5799c31)
- Bug 1259674 Part 3 rename InternalFocusEvent::relatedTarget to InternalFocusEvent::mRelatedTarget r=masayuki (2382b8de82)
- Bug 1256589 part.7 Add PropagationStopped() to WidgetEvent r=smaug (09325f188d)
- Bug 1259663 - Clean up WidgetMouseScrollEvent. r=masayuki (48e1389e22)
- Bug 1263782 - Kill off the deprecated nsINode::IsInDoc(). r=baku (ae80ec21f7)
- Bug 1223751 - Assume all frames are visible in subdocuments of a top level content document that's assuming all frames are visible. r=tn (3b4f99b2fc)
- Bug 1223747 - Don't assume all frames are visible in XUL documents. r=tn (7f26104ff8)
- Bug 1180267 - Switch Fennec over to using the MobileViewportManager for computing the CSS viewport. r=snorp (b85a3a5fb7)
- put back some SPS stuff (960414c383)
- Bug 1151152 - Change behavior when pointer is dragged out of the document. r=smaug (64779386f0)
- Bug 1263787 - Kill off the deprecated nsINode::GetCrossShadowCurrentDoc. r=baku (ed490173ff)
- Bug 1261933 - Continue unlocking pointer even if the widget has gone. r=smaug (93a781887a)
- Bug 1259296 - Do not scroll snap on the main thread for wheel events handled by APZ. r=kats (f72a7fef66)
- Bug 1208371 - Pass parent window to DOMMediaStream through constructor. r=roc (1c2cc7e7c7)
- Bug 1208371 - Make AudioCaptureStream startable. r=padenot (15849ae011)
- Bug 1208371 - Move OnTracksAvailableCallback out of DOMMediaStream. r=roc (a772c949e0)
- Bug 1208371 - Remove unused MediaManager::NotifyMediaStreamTrackEnded. r=jib (9157aa056e)
- Bug 1208371 - Introduce MediaStreamTrack logs. r=roc,jib (72ff4d4c59)
- Bug 1208371 - Track original track in MediaStreamTrack clones. r=jib (39e9ae1200)
- Bug 1208371 - Un-nest MediaEngineSource::PhotoCallback. r=roc (b1e0b48012)
- Bug 1253333: Don't cause a second MediaManager to be created if there's a race with NotifyFinished r=jib (d22d6d01e5)
- Bug 1251357 - fix regression where last MediaStreamTrack.stop did not turn off the camera light. r=jesup (11b4880c02)
- Bug 1208371 - Add a MediaStreamTrackSource interface. r=roc (bb1880524b)
- Bug 1208371 - Add MediaStreamTrackSourceGetter interface. r=roc (c57a1c6a74)
- Bug 1208371 - Let MediaStreamTracks know their TrackID at the source. r=roc (62739d72bf)
- Bug 1259236 - throw NotSupportedError on pc.addTrack of track in constructed stream. r=bwc (8b828e80e4)
- Bug 1208371 - Let FindOwnedDOMTrack operate on input stream. r=roc (26c24ad3c7)
- Bug 1208371 - Add some MediaStreamTrack helper methods. r=roc (1627cd36f8)
- Bug 1208371 - Count the users of a MediaStream to ease Destroy() resonsibility. r=roc (613d446b39)
- bug 1223670 assert that connected streams have the same graph r=padenot (17ea987392)
- Bug 1208371 - Add convenience method for checking if TrackID is explicit. r=roc (32c9d5fe45)
- Bug 1208371 - Allow MediaInputPorts mapped to a destination TrackID. r=roc (71d3c66c4d)
- Bug 1208371 - Remove obsolete SetTrackEnabled() from DOMMediaStream r=roc (cbcf54a342)
- Bug 1208371 - Add MediaStreamTrack::Graph(). r=jib (044b348267)
- Bug 1208371 - Add DOMMediaStream::GetTrackById/GetOwnedTrackById. r=jib (6f58360808)
- Bug 1208371 - Add a generic PrincipalChangeObserver interface. r=mt (79dfb91a89)
- Bug 1208371 - Turn DOMMediaStream::PrincipalChangeObserver into PrincipalChangeObserver<DOMMediaStream>. r=mt (41fdd835de)
- Bug 1208371 - Add principal APIs to MediaStreamTrack. r=mt (a8aab0e0d9)
- Bug 1208371 - Create MediaStreamTrackSource::Sink that forwards principal changes from the source. r=mt (d9fefd4e3c)
- Bug 1208371 - Let DOMMediaStream base its principal on the tracks it contains. r=mt (0c046c7c20)
- Bug 1208371 - Add an interface DecoderPrincipalChangeObserver to HTMLMediaElement. r=roc (ecde0ddae0)
- Bug 1208371 - Make HTMLMediaElement::CaptureStream pass its principal to MediaStreamTrack. r=mt (03ccd4ab12)
- Bug 1208371 - Make MediaManager pass its principal to MediaStreamTrack for gUM. r=mt (5a4b1eb0bc)
- Bug 1208371 - Make ImageCapture listen to principal changes of MediaStreamTrack instead. r=mt (d29fc5c36e)
- Bug 1208371 - Break PCImpl::SetRemoteDescription into smaller pieces. r=mt,bwc (e4337cc413)
- Bug 1208371 - Make PeerConnectionImpl pass its principal to MediaStreamTrack through a new RemoteTrackSource. r=mt (9a7c744a19)
- Bug 1208371 - Make remaining DOMMediaStream principal sources use MediaStreamTrack. r=mt (b30919c681)
- Bug 1208371 - Remove some no longer necessary principal methods on DOMMediaStream. r=mt (5279935d2b)
- Bug 1208371 - Add a MediaStreamTrackListener to MediaStreamGraph. r=roc (5d7048793e)
- Bug 1208371 - Move ImageCapture to a MediaStreamTrackListener. r=roc (98d081b034)
- Bug 1208371 - Make it possible to look up stream id by track in PeerConnectionImpl. r=jib (c65d07cb55)
- Bug 1208371 - Fix DOMMediaStream::OwnsTrack. r=roc (092dad3654)
- Bug 1208371 - Remove MediaStreamTrack::GetStream. r=jib (a78f873d89)
- Bug 1208371 - Route ApplyConstraints through MediaStreamTrackSource. r=jib (32dfd76245)
- Bug 1208371 - Kill nsDOMUserMediaStream with fire. r=jib (9550ad61be)
- Bug 1208371 - Make it possible to block tracks in a MediaInputPort initally. r=roc (39fdd2322f)
- Bug 1208371 - Implement MediaStreamTrack::Clone(). r=smaug,jib,roc (1995b87f10)
- Bug 1208371 - Implement DOMMediaStream::Clone() r=smaug,jib,roc (d445dffd30)
- Bug 1208371 - Various cleanups in DOMMediaStream/MediaStreamTrack. r=jib (a174781a1b)
- Bug 1208371 - Forward input stream and track id on regular track changes for union streams. r=roc (36ed98f3e4)
- Bug 1208371 - Move track.stop() helpers to MediaStreamPlayback. r=jib (123bcd8192)
- Bug 1240478 - Add test for video size on 'loadedmetadata'. r=jesup,jib Bug 1208371 - Test DOMMediaStream::Clone(). r=jib (5ff908fcad)
- Bug 1208371 - Test DOMMediaStream::Clone(). r=jib (445556e6b5)
- Bug 1208371 - Rename CreateOwnDOMTrack/CreateClonedDOMTrack to CreateDOMTrack/CloneDOMTrack. r=jib (41c71b073c)
- Bug 1208371 - Resolve ambiguous symbol MediaStreamTrack. r=bwc (f998354d96)
- Bug 1208371 - Test MediaStreamTrack::Clone(). r=jib (34865902b0)
- Bug 1208371 - Add CORSMode methods to MediaStreamTrack. r=mt (2244b06567)
- Bug 1208371 - Add convenience method for forwarding a track to an MSG-stream. r=roc (f21675220b)
- bug 1223670 throw not supported when creating a node from a stream with different channel r=baku (55e1d67356)
- Bug 1208371 - Lock MediaStreamAudioSourceNode onto the first AudioStreamTrack in mInputStream at time of construction. r=mt,padenot (77640fa752)
- Bug 1208371 - Move HTMLMediaElement::CaptureStream to forward CORSMode changes through MediaStreamTrackSource. r=mt (aa533e8945)
- Bug 1208371 - Remove CORSMode methods from DOMMediaStream. r=mt (41447ccea1)
- Bug 1208371 - Change HTMLMediaElement video sinks to check principal for video only tracks. r=mt (b8168de792)
- Bug 1208371 - Remove ref counting from DOMMediaStream::TrackListener. r=roc (fe275597ca)
- Bug 1208371 - Update tests to accomodate the fact that MediaStreamAudioSourceNodes lock onto the first audio track. r=padenot (ee97d9f274)
- bug 916387 remove workarounds in tests r=padenot (0285f2b98a)
- Bug 1208371 - Modify WebAudio source nodes tests to wait for tracks befoure measuring nr of samples. r=padenot (bb3afcdd12)
- Bug 1208371 - Make PeerIdentity RefCounted. r=mt (74320ca464)
- Bug 1208371 - Add GetPeerIdentity() to MediaStreamTrackSource. r=mt (77bffa988f)
- Bug 1208371 - Add PeerIdentity to LocalTrackSource for gUM tracks. r=mt (4b4163b66d)
- Bug 1208371 - Move PeerConnection to use PeerIdentity on MediaStreamTrack. r=mt (30b5ab4b5d)
- Bug 1208371 - Remove PeerIdentity from DOMMediaStream. r=mt (0d6b66614e)
- Bug 1208371 - Add convenience method for checking track forwarding to MediaStreamTrack. r=roc (d9eb5210e0)
- Bug 1208371 - Remove test_mediarecorder_record_nosrc.html. r=roc (5d7b83b51c)
- Bug 1208371 - Inline MediaRecorder::Session::SetupStreams. r=roc (1f8463266e)
- Bug 1153690 - Release the Session and MediaRecorder objects correctly if there is no ExtractRunnable running. r=roc (424117e697)
- Bug 1257318: Move MediaRecorder to use DirectListeners wherever possible. r=roc (1d6336641b)
- Bug 1208371 - Check principal per track instead of per stream in MediaRecorder. r=roc (946080d5e7)
- Bug 1219711 - Add mochitest for track disabling over a peer connection. r=jib (ad345e05e2)
- Bug 1166832 - Add test to verify audio (using AudioStreamAnalyser) after renegotiation. r=bwc (ad11535ee3)
- Bug 1250934: Make tests use the correct audio frequency when fake devices are used r=drno (1b8cb18b8f)
- Bug 1182426 - Test that we can record CanvasCaptureMediaStreams. r=roc (e65a5dce61)
- Bug 1219711 - Refactor captureStream_common.js to accept generic pixel testing method. r=jib (e55194c8f5)
- Bug 1223696 - Make canvas captureStream helper resilient to exceptions when there's no video. r=roc (215711b89d)
- Bug 1223696. Don't destroy VideoFrameContainer when we reach MetadataLoaded without a video track. r=jwwang (2c83a23b24)
- Bug 1166832 - Add test for canvas capture on multiple streams. r=bwc (592db27fc4)
- Bug 1224029: ensure video elements that may be disabled (black) are scaled r=mattwoodrow (bfef6af17d)
- Bug 1208371 - Simplify track disabling test code. r=jib (771d560f40)
- Bug 1208371 - Test disabling track across peerconnection not affecting clones. r=jib (31913dd9e6)
- Bug 1208371 - Add DirectTrackLister to MediaStreamGraph and MediaStreamTrack. r=roc,jesup (ae86375502)
- Bug 1208371 - Switch MediaPipeline to use direct listeners on tracks. r=jesup,bwc (aa32ce43e1)
- Bug 1208371 - Let PeerConnection consume principals from tracks instead of streams. r=mt (825d6775d0)
- Bug 1257318: Make recorder.pause()/resume() work with DirectListeners r=padenot (65ff6bff4e)
- Bug 1208371 - Move FindTrack from SourceMediaStream to MediaStream. r=roc (5725a863ae)
- Bug 1208371 - Switch MediaStreamTrack to enable/disable tracks on owned stream. r=jesup (ea7445369d)
- Bug 1208371 - Make GraphDriver callback log calls verbose. r=padenot (a502ceea55)
- Bug 1208371 - Do not require DOMLocalMediaStream for gUM (audioCapture). r=jib (c27ee1c308)
- Bug 1208371 - Add a track getter to gUM for fake tracks. r=jib (4958d2b392)
- Bug 1208371 - Remove unnecessary fakeTracks:true from test_streams_individual_pause.html. r=jib (2437b932b2)
- Bug 1208371 - Remove debug canvas from addTrackRemoveTrack test to help android perf. r=padenot (38b1ad91e8)
- Bug 1208371 - Remove debug canvas from peerConnection_trackDisabling test to help android perf. r=padenot (a8b5a62ad6)
- Bug 1208371 - Enable test_peerConnection_trackDisabling.html on android 4.3 debug. r=jib (19e14785f3)
- Bug 1208371 - Remove debug canvas from mediaStreamConstructors test to help android perf. r=padenot (b7e3280b68)
- Bug 1208371 - Add disconnect method to test helper AudioStreamAnalyser to improve performance on slow devices. r=padenot (2a18d693a7)
- Bug 1208371 - Guard LoadManagerSingleton with a WeakPtr. r=pkerr (0f6d425cf4)
- Bug 1208371 - Clear output canvas on each drawImage(). r=jib (45731c0c9a)
- Bug 1208371 - Wait for "loadedmetadata" before setting up nodes in test_mediaElementAudioSourceNodeCrossOrigin.html. r=padenot (8e9398fe88)
- Bug 1181051 - Remove BOM from dom/media/test/test*.html. r=kinetik (c98b2c9b96)
- Bug 1070110 - Ensure the testcase will receive at least 2 blob. r=jwwang (028cb09b08)
- Bug 1208371 - Improve reliability of test_mediarecorder_record_timeslice.html. r=jwwang (c89bd3fe0b)
- Bug 1251494 - Remove remaining references to MOZILLA_XPCOMRT_API from dom. r=jesup (5b8e7ef603)
- Bug 1234230: Don't pass null images for video encoding, and don't reencode the same image r=roc (eb09c07d8d)
- Bug 1258567 - per comment 3, ensure volume is only applied once to the AudioSegment. r=kinetik. (a7e184e483)
- Bug 1250934: Modify MediaEngine shutdown to allow neutering the AudioDataListener r=padenot (70f4831508)
- Bug 1208371 - Add PrincipalHandle to MediaChunks. r=mt,jesup (0662c26a97)
- Bug 1208371 - Add NotifyPrincipalHandleChanged to MediaStreamTrackListener. r=mt,jesup (54d896b1ff)
- Bug 1208371 - Add a PrincipalHandleListener to MediaStreamTrack. r=mt,jesup (9b4d2e0996)
- Bug 1256520 - use SyncRunnable to create DecodedStreamData synchronously to ensure the creation and destruction of DecodedStreamData happen in order. r=kikuo. (7d28eaebba)
- Bug 1237482 - Remove MediaDecoderStateMachine::mStreamSink. r=kikuo. (609efed70f)
- Bug 1208371 - Hook up DecodedStream with PrincipalHandle. r=mt,jwwang (9290efb216)
- Bug 1208371 - Hook up MediaPipeline with PrincipalHandle. r=mt,bwc (b00ed70ddc)
- Bug 1208371 - Ensure DOMMediaStream principals reflect what could reside in their playback streams. r=mt,jesup (bd3e32ef16)
- Bug 1208371 - Ensure a media element's ImageContainer is protected when playing a stream. r=mt,jesup (7914faa5dc)
- Bug 1208371 - Forward declare MediaStreamTrack in MediaPipeline.h. r=bwc (4c88b79137)
- Bug 1208371 - Clean up unnecessary virtuals in MediaPipeline. r=bwc (b7112f4b34)
- Bug 1208371 - Move MediaStreamTrack's label to MediaStreamTrackSource. r=jib (045bfcc738)
- Bug 1259590 - Remove B2G ACL code. r=khuey (e13223c0f6)
- Bug 1265452 followup - Make mGlyphExtentsArray an array of UniquePtr. r=jrmuizel (d12ce46244)
- Bug 1250540 - have media.navigator.permission.disabled no longer bypass media.getusermedia.screensharing.allowed_domains. r=jesup (e55aa02580)
- Bug 1247806 - Fix HSTS redirect check in WebSocketChannel, r=mcmanus (4e44becf9e)
- Bug 1251530: Use 'MakeUnique' instead of 'new' to populate UniquePtr variable in AddCSSValuePair. r=jwatt (286bf2289a)
- Bug 1264787: Make nsCSSValue's AdoptListValue & AdoptPairListValue methods take a UniquePtr. r=heycam (0f7dc7164f)
- Bug 1151243 part 3 - [css-grid] Add a generic nsHTMLReflowState::STATIC_POS_IS_CB_ORIGIN flag to place the static-position at the CB origin, and make nsAbsoluteContainingBlock use it in Grid containers where the placeholder is a child too. r=dholbert (7bde498cc4)
- Bug 1151243 part 4 - Some code cleanup in nsHTMLReflowState::CalculateHypotheticalPosition, and make a few methods 'const' (idempotent patch). r=dholbert (e430f9b99d)
- Bug 1250540 - fix string-parsing regression that made screen share whitelisting stop working. r=jesup (f8af7c3053)
- Bug 1265641: Move AEC tail length and delay-agnostic settings to getUserMedia r=padenot (3feaf716ee)
- Bug 950936 - Introduce ui.popup.disable_autohide pref to ease debugging popups. r=neil (bc28dcd531)
- bug 1211892 read -unico-border-gradient of early Unico versions for ThreeDHighlight and ThreeDShadow r=acomminos (39c83b1865)
- bug 1257695 disable GTK3 deprecation warnings in widget/gtk r=acomminos (1ad2ba0271)
- Bug 1258989 - Switch gtk3drawing to a C++ file, r=karlt (55b8ea3cdf)
- Bug 1259433 - remove gtk_widget_style_get() from gtk3drawing, r=karlt a=kwierso (a9fc049140)
- Bug 1266680 - Rename BlockTrackId to BlockSourceTrackId. r=jesup (5066bbf870)
- Bug 1262808: Refactor |PrincipalID| to |PrincipalHandle| in Gonk media code, r=rjesup (49ec99e852)
- Bug 1144096 part 1 - [css-grid] Refactor nsGridContainerFrame state and methods. r=dholbert (502fb7d424)
- Bug 1144096 part 2 - [css-grid] Make GridItemInfo::mFrame available also in non-DEBUG builds since we'll need it to support fragmentation. r=dholbert (fe0b396741)
- Bug 1144096 part 3 - [css-grid] Remove CellMap::ClearOccupied() since it's not needed anymore. r=dholbert (7aeacdccab)
- Bug 1144096 part 4 - [css-grid] Move more local nsGridContainerFrame classes into .cpp file. r=dholbert (896fc410ac)
- Bug 1144096 part 5 - [css-grid] Create a couple of Grid container frame bits. r=dholbert (abd007fd90)
- Bug 1144096 part 6 - [css-grid] Add support for creating Grid container continuations and deal with overflow containers. r=dholbert (3f02728f24)
- Bug 1144096 part 7 - [css-grid] Don't create PageBreakFrames inside a Grid container. The container will handle forced breaks on its items. r=dholbert (5a0c64369c)
- Bug 1144096 part 8 - [css-grid] Add a new state flag, eBreakBefore, to record where breaks occur between tracks. r=dholbert (5f5d8761c6)
- Bug 1144096 part 9 - [css-grid] Create a SharedGridData object owned by the first-in-flow Grid container to share state between its continuations. r=dholbert (7e510f0bf0)
- Bug 1144096 part 10 - [css-grid] Add a few helper methods to do a break before a row, and resize a row. r=dholbert (d3fec771a6)
- Bug 1144096 part 11 - [css-grid] Add a GetNearestFragmentainer() method that collects some data from the nearest enclosing fragmentainer needed for fragmentation. r=dholbert (20d4736a1c)
- Bug 1144096 part 12 - [css-grid] Collect and merge child frames we need for reflow. r=dholbert (3b5c619f34)
- Bug 1144096 part 13 - [css-grid] Refactor ReflowChildren() by separating out the code that reflows normal flow children (grid items and placeholders) into a new method ReflowInFlowChild(). r=dholbert (ba8a1edc7b)
- Bug 1144096 part 14 - [css-grid] Make ReflowInFlowChild() deal with a constrained available block-size. r=dholbert (292df4167a)
- Bug 1144096 part 15 - [css-grid] Compute our pre-reflow logical skip sides and cache the result of ComputedLogicalBorderPadding() with that applied. r=dholbert (0c7f59389e)
- Bug 1144096 part 16 - [css-grid] Implement fragmentation. r=dholbert (af8b8aac41)
- Bug 1144096 part 17 - [css-grid] Add helper methods that add a sorted list of child frames to the Overflow and ExcessOverflowContainers child lists. r=dholbert (a6c80c9e9c)
- Bug 1144096 part 18 - [css-grid] Fix a couple of bugs in how we handle child existing continuations when pushing/pulling children. r=dholbert (b8a62fab65)
- Bug 1144096 part 19 - [css-grid] Sanity check the initial child lists we get from the frame constructor (DEBUG only). r=dholbert (f56955c3ec)
- Bug 1144096 part 20 - [css-grid] Sanity check our child lists before starting a Reflow (DEBUG only). r=dholbert (d89c771238)
- Bug 1144096 part 21 - [css-grid] Deal with dynamically inserted/appended/removed child frames. r=dholbert (a07ecc06e2)
- Bug 1144096 part 22 - [css-grid] Check NS_INLINE_IS_BREAK_BEFORE before checking other completion status. r=dholbert (731dcd0ac5)
- Bug 1144096 part 23 - [css-grid] A grid container fragment that is an overflow container can't be INCOMPLETE, only OVERFLOW_INCOMPLETE and it should always have zero BSize. r=dholbert (afcd6c1741)
- Bug 1144096 part 24 - [css-grid] Move the child frame merging code at the start of ReflowOverflowContainerChildren into a new method: DrainExcessOverflowContainersList. Make both take a param so that we can override how the OC child lists are merged together (normally just an Append; MergeSortedFrameLists for Grid). r=dholbert (95323b0a56)
- Bug 1144096 part 25 - [css-grid] Enable fragmentation to occur by reporting our actual reflow status. r=dholbert (767c389238)
- Bug 1144096 part 26 - [css-grid] Fragmentation reftests. (07e62f8fd6)
- Bug 1244006 - Use const instead of MOZ_CONSTEXPR to avoid startup crash; r=dbaron (39b0bb32fb)
- Bug 1053986 - Fix comment referring to a GetBorder method that no longer exists. r=dholbert (2cde22a42c)
- Bug 1264784 - part 1 - call nsIFrame::{GetScreenRect,GetView} instead of their *External counterparts; r=dholbert (a2cd84b7a2)
- Bug 963238: Support isTypeSupported() in MediaRecorder, and throw on invalid mimetypes at construction r=cpearce,khuey (5a3709e45f)
- Bug 963238: Fix compilation errors. r=me (09b541337f)
- Bug 1157654 - 1. Do not call MediaRecorder.stop immediately after MediaRecorder.start, because we want to receive an onerror callback. 2. Make the NofityError async. r=roc (ad56918042)
- Bug 1128448 - 1. Close the pref media.encoder.omx.enabled for newer android version. 2. Modify testcase and MR to eusure no timing issue. r=roc (9dfff44a2c)
- Bug 1197669 - Part3 - Ensure the start event comes before any blobs. r=jwwang (d4e950fa49)
- Bug 1225327 - fix |mEncoder| null-dereference. r=jwwang (5ffce18e33)
- Bug 1260702: Don't crash on input sources from WebAudio in MediaRecorder r=padenot (28bcb80a03)
- Bug 1018299 - Throw security error if principal check fails in MediaRecorder::Start(). r=jib, r=mt (1130168c8d)
- Bug 1261007 - Part 1 - Force to send video sample into encoder if we got the same video sample more than 1 seconds. Enable testcases. r=jolin (5698b39b45)
- Bug 1182426 - Test that changing video resolution of a recorded stream throws an error. r=roc (edaf49f2cf)
- Bug 1261007 - Part 2 - fix test_mediarecorder_webm_support. r=rillian (16ef931949)
- Bug 1250054. Part 1 - implement MediaDecoderReaderWrapper. r=jya. (4b78b4335d)
- Bug 1242841 - Make MDSM::mDecodedAudioEndTime zero-based. r=kikuo. (c6c756d554)
- Bug 1242843 - Make MDSM::mDecodedVideoEndTime zero-based. r=kikuo. (c194178323)
- Bug 1242783. Part 1 - per comment 11, increase mAmpleAudioThresholdUsecs to 2s to avoid audio underrun when BT is connected. r=kinetik. (49567f587e)
- Bug 1242783. Part 2 - per comment 12, buffer only 1s when audio is captured. r=kinetik. (e0f0cd9826)
- Bug 1230641: P1. Use UYVY (YUV422) format in decoders. r=mattwoodrow (17d4de398c)
- Bug 1230641: P2. Increase the video queue size on mac. r=cpearce (e575c85f44)
- Bug 1230641 - Make our NSOpenGLContext opaque when in fullscreen mode. r=mstange (51da9e1e70)
- Bug 1257094 - Remove MediaDecoderStateMachine::DECODER_STATE_DECODING_NONE. r=cpearce. (f0993582c7)
- Bug 1252344. Part 1 - remove unnecessary checks from MediaDecoderStateMachine::NeedToDecodeVideo(). r=bechen. (1c9b7aadf4)
- Bug 1252344. Part 2 - remove unnecessary checks from MediaDecoderStateMachine::NeedToDecodeAudio(). r=bechen. (6896d7d1f6)
- Bug 1252360 - remove some check from NeedToDecodeAudio(). r=bechen. (1c064a9985)
- Bug 1252762 - Decode at most one audio/video sample before finishing seeking. r=cpearce. (73ec7691df)
- Bug 1253928 - adjust the time passed to RequestVideoData() by the start time to avoid incorrectly skipping key frames. r=jya. (1bc203d8e6)
- Bug 1252766 - Remove MediaDecoderStateMachine::mDecodeToSeekTarget which is never read. r=kaku. (aac1fe8018)
- Bug 1251460 - MDSM now waits on a promise to enqueue first frame loaded. r=jya (0e7e67e006)
- Bug 1257013 - Part 3: Stop checking for corrupt frames in MediaDecoderStateMachine since we no longer produce them. r=ajones (fbbbabafbb)
2024-04-24 23:53:16 +08:00

2893 lines
84 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -*- 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 "AccCollector.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#include "nsAccUtils.h"
#include "nsAccessibilityService.h"
#include "ApplicationAccessible.h"
#include "nsEventShell.h"
#include "nsTextEquivUtils.h"
#include "DocAccessibleChild.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 "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(Accessible,
mContent, mParent, mChildren)
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), mChildrenFlags(eChildrenUninitialized),
mStateFlags(0), mContextFlags(0), mType(0), mGenericTypes(0),
mRoleMapEntry(nullptr)
{
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()) {
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);
}
}
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->IsBoxFrame()) {
const nsStyleXUL* xulStyle = frame->StyleXUL();
if (xulStyle && frame->IsBoxFrame()) {
// In XUL all boxes are either vertical or horizontal
if (xulStyle->mBoxOrient == NS_STYLE_BOX_ORIENT_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.
if (!mRoleMapEntry || mRoleMapEntry->roleRule == kUseNativeRole ||
mRoleMapEntry->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(rootRect);
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 (mRoleMapEntry) {
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);
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;
}
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.
if (mRoleMapEntry) {
if (mRoleMapEntry->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(mRoleMapEntry->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.
if (mRoleMapEntry && !(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 (mRoleMapEntry->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);
if (mRoleMapEntry) {
// 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 (mRoleMapEntry->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 (!mRoleMapEntry)
return;
*aState |= mRoleMapEntry->state;
if (aria::MapToState(mRoleMapEntry->attributeMap1, element, aState) &&
aria::MapToState(mRoleMapEntry->attributeMap2, element, aState) &&
aria::MapToState(mRoleMapEntry->attributeMap3, element, aState))
aria::MapToState(mRoleMapEntry->attributeMap4, element, aState);
// ARIA gridcell inherits editable/readonly states from the grid until it's
// overridden.
if ((mRoleMapEntry->Is(nsGkAtoms::gridcell) ||
mRoleMapEntry->Is(nsGkAtoms::columnheader) ||
mRoleMapEntry->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)
{
if (!mRoleMapEntry)
return;
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 textbox is a textified subtree.
if (mRoleMapEntry->Is(nsGkAtoms::textbox)) {
nsTextEquivUtils::GetTextEquivFromSubtree(this, aValue);
return;
}
// Value of combobox is a text of current or selected item.
if (mRoleMapEntry->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)
{
if (!mRoleMapEntry || mRoleMapEntry->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
{
return mRoleMapEntry && mRoleMapEntry->IsOfType(eLandmark) ?
*(mRoleMapEntry->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();
// 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 (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::LISTITEM ||
mRoleMapEntry->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 (mRoleMapEntry &&
(mRoleMapEntry->role == roles::OUTLINEITEM ||
mRoleMapEntry->role == roles::LISTITEM ||
mRoleMapEntry->role == roles::ROW ||
mRoleMapEntry->role == roles::OUTLINE ||
mRoleMapEntry->role == roles::LIST ||
mRoleMapEntry->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());
default:
return Relation();
}
}
void
Accessible::GetNativeInterface(void** aNativeAccessible)
{
}
void
Accessible::DoCommand(nsIContent *aContent, uint32_t aActionIndex)
{
class Runnable final : public nsRunnable
{
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;
InvalidateChildren();
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::BindToParent(Accessible* aParent, uint32_t aIndexInParent)
{
NS_PRECONDITION(aParent, "This method isn't used to set null parent!");
if (mParent) {
if (mParent != aParent) {
NS_ERROR("Adopting child!");
mParent->InvalidateChildrenGroupInfo();
mParent->RemoveChild(this);
} else {
NS_ERROR("Binding to the same parent!");
return;
}
}
mParent = aParent;
mIndexInParent = aIndexInParent;
#ifdef DEBUG
AssertInMutatingSubtree();
#endif
// 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()
{
#ifdef DEBUG
AssertInMutatingSubtree();
#endif
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);
}
}
void
Accessible::InvalidateChildren()
{
int32_t childCount = mChildren.Length();
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
mChildren.ElementAt(childIdx)->UnbindFromParent();
}
mEmbeddedObjCollector = nullptr;
mChildren.Clear();
SetChildrenFlag(eChildrenUninitialized);
}
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;
for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
"Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx;
}
mEmbeddedObjCollector = nullptr;
}
if (!nsAccUtils::IsEmbeddedObject(aChild))
SetChildrenFlag(eMixedChildren);
aChild->BindToParent(this, aIndex);
return true;
}
bool
Accessible::RemoveChild(Accessible* aChild)
{
if (!aChild)
return false;
if (aChild->mParent != this || aChild->mIndexInParent == -1)
return false;
uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
if (index >= mChildren.Length() || mChildren[index] != aChild) {
NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
aChild->UnbindFromParent();
return false;
}
for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
"Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx - 1;
}
aChild->UnbindFromParent();
mChildren.RemoveElementAt(index);
mEmbeddedObjCollector = nullptr;
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");
#ifdef DEBUG
// AutoTreeMutation should update group info.
AssertInMutatingSubtree();
#endif
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]->mInt.mIndexOfEmbeddedChild = -1;
}
}
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 (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector->Count();
}
return ChildCount();
}
Accessible*
Accessible::GetEmbeddedChildAt(uint32_t aIndex)
{
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
mEmbeddedObjCollector->GetAccessibleAt(aIndex) : nullptr;
}
return GetChildAt(aIndex);
}
int32_t
Accessible::GetIndexOfEmbeddedChild(Accessible* aChild)
{
if (IsChildrenFlag(eMixedChildren)) {
if (!mEmbeddedObjCollector)
mEmbeddedObjCollector = new EmbeddedObjCollector(this);
return mEmbeddedObjCollector ?
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() && nsAccUtils::IsEmbeddedObject(this);
}
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.
if (mRoleMapEntry && mRoleMapEntry->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;
}
void
Accessible::CacheChildren()
{
NS_ENSURE_TRUE_VOID(Document());
TreeWalker walker(this);
Accessible* child = nullptr;
while ((child = walker.Next()) && AppendChild(child));
}
void
Accessible::TestChildCache(Accessible* aCachedChild) const
{
#ifdef DEBUG
int32_t childCount = mChildren.Length();
if (childCount == 0) {
NS_ASSERTION(IsChildrenFlag(eChildrenUninitialized),
"No children but initialized!");
return;
}
Accessible* child = nullptr;
for (int32_t childIdx = 0; childIdx < childCount; childIdx++) {
child = mChildren[childIdx];
if (child == aCachedChild)
break;
}
NS_ASSERTION(child == aCachedChild,
"[TestChildCache] cached accessible wasn't found. Wrong accessible tree!");
#endif
}
void
Accessible::EnsureChildren()
{
NS_ASSERTION(!IsDefunct(), "Caching children for defunct accessible!");
if (!IsChildrenFlag(eChildrenUninitialized))
return;
// State is embedded children until text leaf accessible is appended.
SetChildrenFlag(eEmbeddedChildren); // Prevent reentry
CacheChildren();
}
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
{
if (!mRoleMapEntry || mRoleMapEntry->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.
if (mRoleMapEntry &&
mRoleMapEntry->actionRule != eNoAction)
return mRoleMapEntry->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();
SetDirtyGroupInfo(false);
}
return mBits.groupInfo;
}
mBits.groupInfo = AccGroupInfo::CreateGroupInfo(this);
return mBits.groupInfo;
}
void
Accessible::InvalidateChildrenGroupInfo()
{
uint32_t length = mChildren.Length();
for (uint32_t i = 0; i < length; i++) {
Accessible* child = mChildren[i];
child->SetDirtyGroupInfo(true);
}
}
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(eLastChildrenFlag <= (1 << kChildrenFlagsBits) - 1,
"Accessible::mChildrenFlags was oversized by eLastChildrenFlag!");
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!");
}
void
Accessible::AssertInMutatingSubtree() const
{
if (IsDoc() || IsApplication())
return;
const Accessible *acc = this;
while (!acc->IsDoc() && !(acc->mStateFlags & eSubtreeMutating)) {
acc = acc->Parent();
if (!acc)
return;
}
MOZ_ASSERT(acc->mStateFlags & eSubtreeMutating);
}
////////////////////////////////////////////////////////////////////////////////
// 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(MOZ_UTF16("MODIFIER_SEPARATOR"),
getter_Copies(separator));
nsAutoString modifierName;
if (mModifierMask & kControl) {
keyStringBundle->GetStringFromName(MOZ_UTF16("VK_CONTROL"),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kAlt) {
keyStringBundle->GetStringFromName(MOZ_UTF16("VK_ALT"),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kShift) {
keyStringBundle->GetStringFromName(MOZ_UTF16("VK_SHIFT"),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kMeta) {
keyStringBundle->GetStringFromName(MOZ_UTF16("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);
}