mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
c6e1bfdcf2
- 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)
3481 lines
123 KiB
C++
3481 lines
123 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/dom/TabParent.h"
|
|
|
|
#include "nsFocusManager.h"
|
|
|
|
#include "AccessibleCaretEventHub.h"
|
|
#include "nsIInterfaceRequestorUtils.h"
|
|
#include "nsGkAtoms.h"
|
|
#include "nsContentUtils.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIDOMWindow.h"
|
|
#include "nsIEditor.h"
|
|
#include "nsPIDOMWindow.h"
|
|
#include "nsIDOMChromeWindow.h"
|
|
#include "nsIDOMElement.h"
|
|
#include "nsIDOMDocument.h"
|
|
#include "nsIDOMRange.h"
|
|
#include "nsIHTMLDocument.h"
|
|
#include "nsIDocShell.h"
|
|
#include "nsIDocShellTreeOwner.h"
|
|
#include "nsLayoutUtils.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsFrameTraversal.h"
|
|
#include "nsIWebNavigation.h"
|
|
#include "nsCaret.h"
|
|
#include "nsIBaseWindow.h"
|
|
#include "nsIXULWindow.h"
|
|
#include "nsViewManager.h"
|
|
#include "nsFrameSelection.h"
|
|
#include "mozilla/dom/Selection.h"
|
|
#include "nsXULPopupManager.h"
|
|
#include "nsMenuPopupFrame.h"
|
|
#include "nsIScriptObjectPrincipal.h"
|
|
#include "nsIPrincipal.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsIObjectFrame.h"
|
|
#include "nsBindingManager.h"
|
|
#include "nsStyleCoord.h"
|
|
#include "TabChild.h"
|
|
#include "nsFrameLoader.h"
|
|
|
|
#include "mozilla/ContentEvents.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/EventDispatcher.h"
|
|
#include "mozilla/EventStateManager.h"
|
|
#include "mozilla/EventStates.h"
|
|
#include "mozilla/IMEStateManager.h"
|
|
#include "mozilla/LookAndFeel.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/unused.h"
|
|
#include <algorithm>
|
|
|
|
#ifdef MOZ_XUL
|
|
#include "nsIDOMXULTextboxElement.h"
|
|
#include "nsIDOMXULMenuListElement.h"
|
|
#endif
|
|
|
|
#ifdef ACCESSIBILITY
|
|
#include "nsAccessibilityService.h"
|
|
#endif
|
|
|
|
#ifndef XP_MACOSX
|
|
#include "nsIScriptError.h"
|
|
#endif
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
using namespace mozilla::widget;
|
|
|
|
// Two types of focus pr logging are available:
|
|
// 'Focus' for normal focus manager calls
|
|
// 'FocusNavigation' for tab and document navigation
|
|
LazyLogModule gFocusLog("Focus");
|
|
LazyLogModule gFocusNavigationLog("FocusNavigation");
|
|
|
|
#define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
|
|
#define LOGFOCUSNAVIGATION(args) MOZ_LOG(gFocusNavigationLog, mozilla::LogLevel::Debug, args)
|
|
|
|
#define LOGTAG(log, format, content) \
|
|
if (MOZ_LOG_TEST(log, LogLevel::Debug)) { \
|
|
nsAutoCString tag(NS_LITERAL_CSTRING("(none)")); \
|
|
if (content) { \
|
|
content->NodeInfo()->NameAtom()->ToUTF8String(tag); \
|
|
} \
|
|
MOZ_LOG(log, LogLevel::Debug, (format, tag.get())); \
|
|
}
|
|
|
|
#define LOGCONTENT(format, content) LOGTAG(gFocusLog, format, content)
|
|
#define LOGCONTENTNAVIGATION(format, content) LOGTAG(gFocusNavigationLog, format, content)
|
|
|
|
struct nsDelayedBlurOrFocusEvent
|
|
{
|
|
nsDelayedBlurOrFocusEvent(EventMessage aEventMessage,
|
|
nsIPresShell* aPresShell,
|
|
nsIDocument* aDocument,
|
|
EventTarget* aTarget)
|
|
: mPresShell(aPresShell)
|
|
, mDocument(aDocument)
|
|
, mTarget(aTarget)
|
|
, mEventMessage(aEventMessage)
|
|
{
|
|
}
|
|
|
|
nsDelayedBlurOrFocusEvent(const nsDelayedBlurOrFocusEvent& aOther)
|
|
: mPresShell(aOther.mPresShell)
|
|
, mDocument(aOther.mDocument)
|
|
, mTarget(aOther.mTarget)
|
|
, mEventMessage(aOther.mEventMessage)
|
|
{
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> mPresShell;
|
|
nsCOMPtr<nsIDocument> mDocument;
|
|
nsCOMPtr<EventTarget> mTarget;
|
|
EventMessage mEventMessage;
|
|
};
|
|
|
|
inline void ImplCycleCollectionUnlink(nsDelayedBlurOrFocusEvent& aField)
|
|
{
|
|
aField.mPresShell = nullptr;
|
|
aField.mDocument = nullptr;
|
|
aField.mTarget = nullptr;
|
|
}
|
|
|
|
inline void
|
|
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
|
nsDelayedBlurOrFocusEvent& aField,
|
|
const char* aName,
|
|
uint32_t aFlags = 0)
|
|
{
|
|
CycleCollectionNoteChild(aCallback, aField.mPresShell.get(), aName, aFlags);
|
|
CycleCollectionNoteChild(aCallback, aField.mDocument.get(), aName, aFlags);
|
|
CycleCollectionNoteChild(aCallback, aField.mTarget.get(), aName, aFlags);
|
|
}
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFocusManager)
|
|
NS_INTERFACE_MAP_ENTRY(nsIFocusManager)
|
|
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFocusManager)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFocusManager)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFocusManager)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION(nsFocusManager,
|
|
mActiveWindow,
|
|
mFocusedWindow,
|
|
mFocusedContent,
|
|
mFirstBlurEvent,
|
|
mFirstFocusEvent,
|
|
mWindowBeingLowered,
|
|
mDelayedBlurFocusEvents,
|
|
mMouseButtonEventHandlingDocument)
|
|
|
|
nsFocusManager* nsFocusManager::sInstance = nullptr;
|
|
bool nsFocusManager::sMouseFocusesFormControl = false;
|
|
bool nsFocusManager::sTestMode = false;
|
|
|
|
static const char* kObservedPrefs[] = {
|
|
"accessibility.browsewithcaret",
|
|
"accessibility.tabfocus_applies_to_xul",
|
|
"accessibility.mouse_focuses_formcontrol",
|
|
"focusmanager.testmode",
|
|
nullptr
|
|
};
|
|
|
|
nsFocusManager::nsFocusManager()
|
|
{ }
|
|
|
|
nsFocusManager::~nsFocusManager()
|
|
{
|
|
Preferences::RemoveObservers(this, kObservedPrefs);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->RemoveObserver(this, "xpcom-shutdown");
|
|
}
|
|
}
|
|
|
|
// static
|
|
nsresult
|
|
nsFocusManager::Init()
|
|
{
|
|
nsFocusManager* fm = new nsFocusManager();
|
|
NS_ADDREF(fm);
|
|
sInstance = fm;
|
|
|
|
nsIContent::sTabFocusModelAppliesToXUL =
|
|
Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
|
|
nsIContent::sTabFocusModelAppliesToXUL);
|
|
|
|
sMouseFocusesFormControl =
|
|
Preferences::GetBool("accessibility.mouse_focuses_formcontrol", false);
|
|
|
|
sTestMode = Preferences::GetBool("focusmanager.testmode", false);
|
|
|
|
Preferences::AddWeakObservers(fm, kObservedPrefs);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
obs->AddObserver(fm, "xpcom-shutdown", true);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsFocusManager::Shutdown()
|
|
{
|
|
NS_IF_RELEASE(sInstance);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::Observe(nsISupports *aSubject,
|
|
const char *aTopic,
|
|
const char16_t *aData)
|
|
{
|
|
if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
|
nsDependentString data(aData);
|
|
if (data.EqualsLiteral("accessibility.browsewithcaret")) {
|
|
UpdateCaretForCaretBrowsingMode();
|
|
}
|
|
else if (data.EqualsLiteral("accessibility.tabfocus_applies_to_xul")) {
|
|
nsIContent::sTabFocusModelAppliesToXUL =
|
|
Preferences::GetBool("accessibility.tabfocus_applies_to_xul",
|
|
nsIContent::sTabFocusModelAppliesToXUL);
|
|
}
|
|
else if (data.EqualsLiteral("accessibility.mouse_focuses_formcontrol")) {
|
|
sMouseFocusesFormControl =
|
|
Preferences::GetBool("accessibility.mouse_focuses_formcontrol",
|
|
false);
|
|
}
|
|
else if (data.EqualsLiteral("focusmanager.testmode")) {
|
|
sTestMode = Preferences::GetBool("focusmanager.testmode", false);
|
|
}
|
|
} else if (!nsCRT::strcmp(aTopic, "xpcom-shutdown")) {
|
|
mActiveWindow = nullptr;
|
|
mFocusedWindow = nullptr;
|
|
mFocusedContent = nullptr;
|
|
mFirstBlurEvent = nullptr;
|
|
mFirstFocusEvent = nullptr;
|
|
mWindowBeingLowered = nullptr;
|
|
mDelayedBlurFocusEvents.Clear();
|
|
mMouseButtonEventHandlingDocument = nullptr;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// given a frame content node, retrieve the nsIDOMWindow displayed in it
|
|
static nsPIDOMWindow*
|
|
GetContentWindow(nsIContent* aContent)
|
|
{
|
|
nsIDocument* doc = aContent->GetComposedDoc();
|
|
if (doc) {
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
|
if (subdoc)
|
|
return subdoc->GetWindow();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// get the current window for the given content node
|
|
static nsPIDOMWindow*
|
|
GetCurrentWindow(nsIContent* aContent)
|
|
{
|
|
nsIDocument* doc = aContent->GetComposedDoc();
|
|
return doc ? doc->GetWindow() : nullptr;
|
|
}
|
|
|
|
// static
|
|
nsIContent*
|
|
nsFocusManager::GetFocusedDescendant(nsPIDOMWindow* aWindow, bool aDeep,
|
|
nsPIDOMWindow** aFocusedWindow)
|
|
{
|
|
NS_ENSURE_TRUE(aWindow, nullptr);
|
|
|
|
*aFocusedWindow = nullptr;
|
|
|
|
nsIContent* currentContent = nullptr;
|
|
nsPIDOMWindow* window = aWindow->GetOuterWindow();
|
|
while (window) {
|
|
*aFocusedWindow = window;
|
|
currentContent = window->GetFocusedNode();
|
|
if (!currentContent || !aDeep)
|
|
break;
|
|
|
|
window = GetContentWindow(currentContent);
|
|
}
|
|
|
|
NS_IF_ADDREF(*aFocusedWindow);
|
|
|
|
return currentContent;
|
|
}
|
|
|
|
// static
|
|
nsIContent*
|
|
nsFocusManager::GetRedirectedFocus(nsIContent* aContent)
|
|
{
|
|
#ifdef MOZ_XUL
|
|
if (aContent->IsXULElement()) {
|
|
nsCOMPtr<nsIDOMNode> inputField;
|
|
|
|
nsCOMPtr<nsIDOMXULTextBoxElement> textbox = do_QueryInterface(aContent);
|
|
if (textbox) {
|
|
textbox->GetInputField(getter_AddRefs(inputField));
|
|
}
|
|
else {
|
|
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aContent);
|
|
if (menulist) {
|
|
menulist->GetInputField(getter_AddRefs(inputField));
|
|
}
|
|
else if (aContent->IsXULElement(nsGkAtoms::scale)) {
|
|
nsCOMPtr<nsIDocument> doc = aContent->GetComposedDoc();
|
|
if (!doc)
|
|
return nullptr;
|
|
|
|
nsINodeList* children = doc->BindingManager()->GetAnonymousNodesFor(aContent);
|
|
if (children) {
|
|
nsIContent* child = children->Item(0);
|
|
if (child && child->IsXULElement(nsGkAtoms::slider))
|
|
return child;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inputField) {
|
|
nsCOMPtr<nsIContent> retval = do_QueryInterface(inputField);
|
|
return retval;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// static
|
|
InputContextAction::Cause
|
|
nsFocusManager::GetFocusMoveActionCause(uint32_t aFlags)
|
|
{
|
|
if (aFlags & nsIFocusManager::FLAG_BYTOUCH) {
|
|
return InputContextAction::CAUSE_TOUCH;
|
|
} else if (aFlags & nsIFocusManager::FLAG_BYMOUSE) {
|
|
return InputContextAction::CAUSE_MOUSE;
|
|
} else if (aFlags & nsIFocusManager::FLAG_BYKEY) {
|
|
return InputContextAction::CAUSE_KEY;
|
|
}
|
|
return InputContextAction::CAUSE_UNKNOWN;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::GetActiveWindow(nsIDOMWindow** aWindow)
|
|
{
|
|
NS_IF_ADDREF(*aWindow = mActiveWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::SetActiveWindow(nsIDOMWindow* aWindow)
|
|
{
|
|
// only top-level windows can be made active
|
|
nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aWindow);
|
|
if (piWindow)
|
|
piWindow = piWindow->GetOuterWindow();
|
|
|
|
NS_ENSURE_TRUE(piWindow && (piWindow == piWindow->GetPrivateRoot()),
|
|
NS_ERROR_INVALID_ARG);
|
|
|
|
RaiseWindow(piWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::GetFocusedWindow(nsIDOMWindow** aFocusedWindow)
|
|
{
|
|
NS_IF_ADDREF(*aFocusedWindow = mFocusedWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsFocusManager::SetFocusedWindow(nsIDOMWindow* aWindowToFocus)
|
|
{
|
|
LOGFOCUS(("<<SetFocusedWindow begin>>"));
|
|
|
|
nsCOMPtr<nsPIDOMWindow> windowToFocus(do_QueryInterface(aWindowToFocus));
|
|
NS_ENSURE_TRUE(windowToFocus, NS_ERROR_FAILURE);
|
|
|
|
windowToFocus = windowToFocus->GetOuterWindow();
|
|
|
|
nsCOMPtr<Element> frameElement = windowToFocus->GetFrameElementInternal();
|
|
if (frameElement) {
|
|
// pass false for aFocusChanged so that the caret does not get updated
|
|
// and scrolling does not occur.
|
|
SetFocusInner(frameElement, 0, false, true);
|
|
}
|
|
else {
|
|
// this is a top-level window. If the window has a child frame focused,
|
|
// clear the focus. Otherwise, focus should already be in this frame, or
|
|
// already cleared. This ensures that focus will be in this frame and not
|
|
// in a child.
|
|
nsIContent* content = windowToFocus->GetFocusedNode();
|
|
if (content) {
|
|
nsCOMPtr<nsIDOMWindow> childWindow = GetContentWindow(content);
|
|
if (childWindow)
|
|
ClearFocus(windowToFocus);
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindow> rootWindow = windowToFocus->GetPrivateRoot();
|
|
if (rootWindow)
|
|
RaiseWindow(rootWindow);
|
|
|
|
LOGFOCUS(("<<SetFocusedWindow end>>"));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::GetFocusedElement(nsIDOMElement** aFocusedElement)
|
|
{
|
|
if (mFocusedContent)
|
|
CallQueryInterface(mFocusedContent, aFocusedElement);
|
|
else
|
|
*aFocusedElement = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::GetLastFocusMethod(nsIDOMWindow* aWindow, uint32_t* aLastFocusMethod)
|
|
{
|
|
// the focus method is stored on the inner window
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
if (window && window->IsOuterWindow())
|
|
window = window->GetCurrentInnerWindow();
|
|
if (!window)
|
|
window = mFocusedWindow;
|
|
|
|
*aLastFocusMethod = window ? window->GetFocusMethod() : 0;
|
|
|
|
NS_ASSERTION((*aLastFocusMethod & FOCUSMETHOD_MASK) == *aLastFocusMethod,
|
|
"invalid focus method");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::SetFocus(nsIDOMElement* aElement, uint32_t aFlags)
|
|
{
|
|
LOGFOCUS(("<<SetFocus begin>>"));
|
|
|
|
nsCOMPtr<nsIContent> newFocus = do_QueryInterface(aElement);
|
|
NS_ENSURE_ARG(newFocus);
|
|
|
|
SetFocusInner(newFocus, aFlags, true, true);
|
|
|
|
LOGFOCUS(("<<SetFocus end>>"));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::ElementIsFocusable(nsIDOMElement* aElement, uint32_t aFlags,
|
|
bool* aIsFocusable)
|
|
{
|
|
NS_ENSURE_TRUE(aElement, NS_ERROR_INVALID_ARG);
|
|
|
|
nsCOMPtr<nsIContent> aContent = do_QueryInterface(aElement);
|
|
|
|
*aIsFocusable = CheckIfFocusable(aContent, aFlags) != nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::MoveFocus(nsIDOMWindow* aWindow, nsIDOMElement* aStartElement,
|
|
uint32_t aType, uint32_t aFlags, nsIDOMElement** aElement)
|
|
{
|
|
*aElement = nullptr;
|
|
|
|
LOGFOCUS(("<<MoveFocus begin Type: %d Flags: %x>>", aType, aFlags));
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug) && mFocusedWindow) {
|
|
nsIDocument* doc = mFocusedWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
nsAutoCString spec;
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Focused Window: %p %s", mFocusedWindow.get(), spec.get()));
|
|
}
|
|
}
|
|
|
|
LOGCONTENT(" Current Focus: %s", mFocusedContent.get());
|
|
|
|
// use FLAG_BYMOVEFOCUS when switching focus with MoveFocus unless one of
|
|
// the other focus methods is already set, or we're just moving to the root
|
|
// or caret position.
|
|
if (aType != MOVEFOCUS_ROOT && aType != MOVEFOCUS_CARET &&
|
|
(aFlags & FOCUSMETHOD_MASK) == 0) {
|
|
aFlags |= FLAG_BYMOVEFOCUS;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window;
|
|
nsCOMPtr<nsIContent> startContent;
|
|
if (aStartElement) {
|
|
startContent = do_QueryInterface(aStartElement);
|
|
NS_ENSURE_TRUE(startContent, NS_ERROR_INVALID_ARG);
|
|
|
|
window = GetCurrentWindow(startContent);
|
|
}
|
|
else {
|
|
window = aWindow ? do_QueryInterface(aWindow) : mFocusedWindow;
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
window = window->GetOuterWindow();
|
|
}
|
|
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
|
|
bool noParentTraversal = aFlags & FLAG_NOPARENTFRAME;
|
|
nsCOMPtr<nsIContent> newFocus;
|
|
nsresult rv = DetermineElementToMoveFocus(window, startContent, aType, noParentTraversal,
|
|
getter_AddRefs(newFocus));
|
|
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
LOGCONTENTNAVIGATION("Element to be focused: %s", newFocus.get());
|
|
|
|
if (newFocus) {
|
|
// for caret movement, pass false for the aFocusChanged argument,
|
|
// otherwise the caret will end up moving to the focus position. This
|
|
// would be a problem because the caret would move to the beginning of the
|
|
// focused link making it impossible to navigate the caret over a link.
|
|
SetFocusInner(newFocus, aFlags, aType != MOVEFOCUS_CARET, true);
|
|
CallQueryInterface(newFocus, aElement);
|
|
}
|
|
else if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_CARET) {
|
|
// no content was found, so clear the focus for these two types.
|
|
ClearFocus(window);
|
|
}
|
|
|
|
LOGFOCUS(("<<MoveFocus end>>"));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::ClearFocus(nsIDOMWindow* aWindow)
|
|
{
|
|
LOGFOCUS(("<<ClearFocus begin>>"));
|
|
|
|
// if the window to clear is the focused window or an ancestor of the
|
|
// focused window, then blur the existing focused content. Otherwise, the
|
|
// focus is somewhere else so just update the current node.
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
window = window->GetOuterWindow();
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
if (IsSameOrAncestor(window, mFocusedWindow)) {
|
|
bool isAncestor = (window != mFocusedWindow);
|
|
if (Blur(window, nullptr, isAncestor, true)) {
|
|
// if we are clearing the focus on an ancestor of the focused window,
|
|
// the ancestor will become the new focused window, so focus it
|
|
if (isAncestor)
|
|
Focus(window, nullptr, 0, true, false, false, true);
|
|
}
|
|
}
|
|
else {
|
|
window->SetFocusedNode(nullptr);
|
|
}
|
|
|
|
LOGFOCUS(("<<ClearFocus end>>"));
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::GetFocusedElementForWindow(nsIDOMWindow* aWindow,
|
|
bool aDeep,
|
|
nsIDOMWindow** aFocusedWindow,
|
|
nsIDOMElement** aElement)
|
|
{
|
|
*aElement = nullptr;
|
|
if (aFocusedWindow)
|
|
*aFocusedWindow = nullptr;
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
window = window->GetOuterWindow();
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
|
nsCOMPtr<nsIContent> focusedContent =
|
|
GetFocusedDescendant(window, aDeep, getter_AddRefs(focusedWindow));
|
|
if (focusedContent)
|
|
CallQueryInterface(focusedContent, aElement);
|
|
|
|
if (aFocusedWindow)
|
|
NS_IF_ADDREF(*aFocusedWindow = focusedWindow);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::MoveCaretToFocus(nsIDOMWindow* aWindow)
|
|
{
|
|
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(aWindow);
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(webnav);
|
|
if (dsti) {
|
|
if (dsti->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
|
nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(dsti);
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
|
|
// don't move the caret for editable documents
|
|
bool isEditable;
|
|
docShell->GetEditable(&isEditable);
|
|
if (isEditable)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aWindow));
|
|
nsCOMPtr<nsIContent> content = window->GetFocusedNode();
|
|
if (content)
|
|
MoveCaretToFocus(presShell, content);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
|
|
{
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
|
|
LOGFOCUS(("Window %p Raised [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
|
|
nsAutoCString spec;
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Raised Window: %p %s", aWindow, spec.get()));
|
|
}
|
|
if (mActiveWindow) {
|
|
doc = mActiveWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Active Window: %p %s", mActiveWindow.get(), spec.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mActiveWindow == window) {
|
|
// The window is already active, so there is no need to focus anything,
|
|
// but make sure that the right widget is focused. This is a special case
|
|
// for Windows because when restoring a minimized window, a second
|
|
// activation will occur and the top-level widget could be focused instead
|
|
// of the child we want. We solve this by calling SetFocus to ensure that
|
|
// what the focus manager thinks should be the current widget is actually
|
|
// focused.
|
|
EnsureCurrentWidgetFocused();
|
|
return NS_OK;
|
|
}
|
|
|
|
// lower the existing window, if any. This shouldn't happen usually.
|
|
if (mActiveWindow)
|
|
WindowLowered(mActiveWindow);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> docShellAsItem = window->GetDocShell();
|
|
// If there's no docShellAsItem, this window must have been closed,
|
|
// in that case there is no tree owner.
|
|
NS_ENSURE_TRUE(docShellAsItem, NS_OK);
|
|
|
|
// set this as the active window
|
|
mActiveWindow = window;
|
|
|
|
// ensure that the window is enabled and visible
|
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
|
docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
|
|
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(treeOwner);
|
|
if (baseWindow) {
|
|
bool isEnabled = true;
|
|
if (NS_SUCCEEDED(baseWindow->GetEnabled(&isEnabled)) && !isEnabled) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
if (!sTestMode) {
|
|
baseWindow->SetVisibility(true);
|
|
}
|
|
}
|
|
|
|
// If this is a parent or single process window, send the activate event.
|
|
// Events for child process windows will be sent when ParentActivated
|
|
// is called.
|
|
if (XRE_IsParentProcess()) {
|
|
ActivateOrDeactivate(window, true);
|
|
}
|
|
|
|
// retrieve the last focused element within the window that was raised
|
|
nsCOMPtr<nsPIDOMWindow> currentWindow;
|
|
nsCOMPtr<nsIContent> currentFocus =
|
|
GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
|
|
|
|
NS_ASSERTION(currentWindow, "window raised with no window current");
|
|
if (!currentWindow)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell();
|
|
if (presShell) {
|
|
// disable selection mousedown state on activation
|
|
// XXXndeakin P3 not sure if this is necessary, but it doesn't hurt
|
|
RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
|
|
frameSelection->SetDragState(false);
|
|
}
|
|
|
|
// If there is no nsIXULWindow, then this is an embedded or child process window.
|
|
// Pass false for aWindowRaised so that commands get updated.
|
|
nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(baseWindow));
|
|
Focus(currentWindow, currentFocus, 0, true, false, xulWin != nullptr, true);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
|
|
{
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
NS_ENSURE_TRUE(window && window->IsOuterWindow(), NS_ERROR_INVALID_ARG);
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
|
|
LOGFOCUS(("Window %p Lowered [Currently: %p %p]", aWindow, mActiveWindow.get(), mFocusedWindow.get()));
|
|
nsAutoCString spec;
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Lowered Window: %s", spec.get()));
|
|
}
|
|
if (mActiveWindow) {
|
|
doc = mActiveWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Active Window: %s", spec.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mActiveWindow != window)
|
|
return NS_OK;
|
|
|
|
// clear the mouse capture as the active window has changed
|
|
nsIPresShell::SetCapturingContent(nullptr, 0);
|
|
|
|
// If this is a parent or single process window, send the deactivate event.
|
|
// Events for child process windows will be sent when ParentActivated
|
|
// is called.
|
|
if (XRE_IsParentProcess()) {
|
|
ActivateOrDeactivate(window, false);
|
|
}
|
|
|
|
// keep track of the window being lowered, so that attempts to raise the
|
|
// window can be prevented until we return. Otherwise, focus can get into
|
|
// an unusual state.
|
|
mWindowBeingLowered = mActiveWindow;
|
|
mActiveWindow = nullptr;
|
|
|
|
if (mFocusedWindow)
|
|
Blur(nullptr, nullptr, true, true);
|
|
|
|
mWindowBeingLowered = nullptr;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::ContentRemoved(nsIDocument* aDocument, nsIContent* aContent)
|
|
{
|
|
NS_ENSURE_ARG(aDocument);
|
|
NS_ENSURE_ARG(aContent);
|
|
|
|
nsPIDOMWindow *window = aDocument->GetWindow();
|
|
if (!window)
|
|
return NS_OK;
|
|
|
|
// if the content is currently focused in the window, or is an ancestor
|
|
// of the currently focused element, reset the focus within that window.
|
|
nsIContent* content = window->GetFocusedNode();
|
|
if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
|
|
bool shouldShowFocusRing = window->ShouldShowFocusRing();
|
|
window->SetFocusedNode(nullptr);
|
|
|
|
// if this window is currently focused, clear the global focused
|
|
// element as well, but don't fire any events.
|
|
if (window == mFocusedWindow) {
|
|
mFocusedContent = nullptr;
|
|
} else {
|
|
// Check if the node that was focused is an iframe or similar by looking
|
|
// if it has a subdocument. This would indicate that this focused iframe
|
|
// and its descendants will be going away. We will need to move the
|
|
// focus somewhere else, so just clear the focus in the toplevel window
|
|
// so that no element is focused.
|
|
nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
|
|
if (subdoc) {
|
|
nsCOMPtr<nsIDocShell> docShell = subdoc->GetDocShell();
|
|
if (docShell) {
|
|
nsCOMPtr<nsPIDOMWindow> childWindow = docShell->GetWindow();
|
|
if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
|
|
ClearFocus(mActiveWindow);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Notify the editor in case we removed its ancestor limiter.
|
|
if (content->IsEditable()) {
|
|
nsCOMPtr<nsIDocShell> docShell = aDocument->GetDocShell();
|
|
if (docShell) {
|
|
nsCOMPtr<nsIEditor> editor;
|
|
docShell->GetEditor(getter_AddRefs(editor));
|
|
if (editor) {
|
|
nsCOMPtr<nsISelection> s;
|
|
editor->GetSelection(getter_AddRefs(s));
|
|
nsCOMPtr<nsISelectionPrivate> selection = do_QueryInterface(s);
|
|
if (selection) {
|
|
nsCOMPtr<nsIContent> limiter;
|
|
selection->GetAncestorLimiter(getter_AddRefs(limiter));
|
|
if (limiter == content) {
|
|
editor->FinalizeSelection();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
NotifyFocusStateChange(content, shouldShowFocusRing, false);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
|
|
{
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
|
|
LOGFOCUS(("Window %p Shown [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
|
|
nsAutoCString spec;
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS(("Shown Window: %s", spec.get()));
|
|
}
|
|
|
|
if (mFocusedWindow) {
|
|
doc = mFocusedWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Focused Window: %s", spec.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nsCOMPtr<nsITabChild> child = do_GetInterface(window->GetDocShell())) {
|
|
bool active = static_cast<TabChild*>(child.get())->ParentIsActive();
|
|
ActivateOrDeactivate(window, active);
|
|
}
|
|
|
|
if (mFocusedWindow != window)
|
|
return NS_OK;
|
|
|
|
if (aNeedsFocus) {
|
|
nsCOMPtr<nsPIDOMWindow> currentWindow;
|
|
nsCOMPtr<nsIContent> currentFocus =
|
|
GetFocusedDescendant(window, true, getter_AddRefs(currentWindow));
|
|
if (currentWindow)
|
|
Focus(currentWindow, currentFocus, 0, true, false, false, true);
|
|
}
|
|
else {
|
|
// Sometimes, an element in a window can be focused before the window is
|
|
// visible, which would mean that the widget may not be properly focused.
|
|
// When the window becomes visible, make sure the right widget is focused.
|
|
EnsureCurrentWidgetFocused();
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::WindowHidden(nsIDOMWindow* aWindow)
|
|
{
|
|
// if there is no window or it is not the same or an ancestor of the
|
|
// currently focused window, just return, as the current focus will not
|
|
// be affected.
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
|
|
LOGFOCUS(("Window %p Hidden [Currently: %p %p]", window.get(), mActiveWindow.get(), mFocusedWindow.get()));
|
|
nsAutoCString spec;
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Hide Window: %s", spec.get()));
|
|
}
|
|
|
|
if (mFocusedWindow) {
|
|
doc = mFocusedWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Focused Window: %s", spec.get()));
|
|
}
|
|
}
|
|
|
|
if (mActiveWindow) {
|
|
doc = mActiveWindow->GetExtantDoc();
|
|
if (doc && doc->GetDocumentURI()) {
|
|
doc->GetDocumentURI()->GetSpec(spec);
|
|
LOGFOCUS((" Active Window: %s", spec.get()));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!IsSameOrAncestor(window, mFocusedWindow))
|
|
return NS_OK;
|
|
|
|
// at this point, we know that the window being hidden is either the focused
|
|
// window, or an ancestor of the focused window. Either way, the focus is no
|
|
// longer valid, so it needs to be updated.
|
|
|
|
nsCOMPtr<nsIContent> oldFocusedContent = mFocusedContent.forget();
|
|
|
|
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
|
|
nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
|
|
|
|
if (oldFocusedContent && oldFocusedContent->IsInComposedDoc()) {
|
|
NotifyFocusStateChange(oldFocusedContent,
|
|
mFocusedWindow->ShouldShowFocusRing(),
|
|
false);
|
|
window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
|
|
if (presShell) {
|
|
SendFocusOrBlurEvent(eBlur, presShell,
|
|
oldFocusedContent->GetComposedDoc(),
|
|
oldFocusedContent, 1, false);
|
|
}
|
|
}
|
|
|
|
nsPresContext* focusedPresContext =
|
|
presShell ? presShell->GetPresContext() : nullptr;
|
|
IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
|
|
GetFocusMoveActionCause(0));
|
|
if (presShell) {
|
|
SetCaretVisible(presShell, false, nullptr);
|
|
}
|
|
|
|
// if the docshell being hidden is being destroyed, then we want to move
|
|
// focus somewhere else. Call ClearFocus on the toplevel window, which
|
|
// will have the effect of clearing the focus and moving the focused window
|
|
// to the toplevel window. But if the window isn't being destroyed, we are
|
|
// likely just loading a new document in it, so we want to maintain the
|
|
// focused window so that the new document gets properly focused.
|
|
bool beingDestroyed;
|
|
nsCOMPtr<nsIDocShell> docShellBeingHidden = window->GetDocShell();
|
|
docShellBeingHidden->IsBeingDestroyed(&beingDestroyed);
|
|
if (beingDestroyed) {
|
|
// There is usually no need to do anything if a toplevel window is going
|
|
// away, as we assume that WindowLowered will be called. However, this may
|
|
// not happen if nsIAppStartup::eForceQuit is used to quit, and can cause
|
|
// a leak. So if the active window is being destroyed, call WindowLowered
|
|
// directly.
|
|
NS_ASSERTION(mFocusedWindow->IsOuterWindow(), "outer window expected");
|
|
if (mActiveWindow == mFocusedWindow || mActiveWindow == window)
|
|
WindowLowered(mActiveWindow);
|
|
else
|
|
ClearFocus(mActiveWindow);
|
|
return NS_OK;
|
|
}
|
|
|
|
// if the window being hidden is an ancestor of the focused window, adjust
|
|
// the focused window so that it points to the one being hidden. This
|
|
// ensures that the focused window isn't in a chain of frames that doesn't
|
|
// exist any more.
|
|
if (window != mFocusedWindow) {
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti =
|
|
mFocusedWindow ? mFocusedWindow->GetDocShell() : nullptr;
|
|
if (dsti) {
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
if (parentDsti) {
|
|
nsCOMPtr<nsPIDOMWindow> parentWindow = parentDsti->GetWindow();
|
|
if (parentWindow)
|
|
parentWindow->SetFocusedNode(nullptr);
|
|
}
|
|
}
|
|
|
|
SetFocusedWindowInternal(window);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::FireDelayedEvents(nsIDocument* aDocument)
|
|
{
|
|
NS_ENSURE_ARG(aDocument);
|
|
|
|
// fire any delayed focus and blur events in the same order that they were added
|
|
for (uint32_t i = 0; i < mDelayedBlurFocusEvents.Length(); i++) {
|
|
if (mDelayedBlurFocusEvents[i].mDocument == aDocument) {
|
|
if (!aDocument->GetInnerWindow() ||
|
|
!aDocument->GetInnerWindow()->IsCurrentInnerWindow()) {
|
|
// If the document was navigated away from or is defunct, don't bother
|
|
// firing events on it. Note the symmetry between this condition and
|
|
// the similar one in nsDocument.cpp:FireOrClearDelayedEvents.
|
|
mDelayedBlurFocusEvents.RemoveElementAt(i);
|
|
--i;
|
|
} else if (!aDocument->EventHandlingSuppressed()) {
|
|
EventMessage message = mDelayedBlurFocusEvents[i].mEventMessage;
|
|
nsCOMPtr<EventTarget> target = mDelayedBlurFocusEvents[i].mTarget;
|
|
nsCOMPtr<nsIPresShell> presShell = mDelayedBlurFocusEvents[i].mPresShell;
|
|
mDelayedBlurFocusEvents.RemoveElementAt(i);
|
|
SendFocusOrBlurEvent(message, presShell, aDocument, target, 0, false);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::FocusPlugin(nsIContent* aContent)
|
|
{
|
|
NS_ENSURE_ARG(aContent);
|
|
SetFocusInner(aContent, 0, true, false);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsFocusManager::ParentActivated(nsIDOMWindow* aWindow, bool aActive)
|
|
{
|
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
|
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
|
|
|
window = window->GetOuterWindow();
|
|
|
|
ActivateOrDeactivate(window, aActive);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* static */
|
|
void
|
|
nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
|
|
bool aWindowShouldShowFocusRing,
|
|
bool aGettingFocus)
|
|
{
|
|
if (!aContent->IsElement()) {
|
|
return;
|
|
}
|
|
EventStates eventState = NS_EVENT_STATE_FOCUS;
|
|
if (aWindowShouldShowFocusRing) {
|
|
eventState |= NS_EVENT_STATE_FOCUSRING;
|
|
}
|
|
if (aGettingFocus) {
|
|
aContent->AsElement()->AddStates(eventState);
|
|
} else {
|
|
aContent->AsElement()->RemoveStates(eventState);
|
|
}
|
|
}
|
|
|
|
// static
|
|
void
|
|
nsFocusManager::EnsureCurrentWidgetFocused()
|
|
{
|
|
if (!mFocusedWindow || sTestMode)
|
|
return;
|
|
|
|
// get the main child widget for the focused window and ensure that the
|
|
// platform knows that this widget is focused.
|
|
nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell();
|
|
if (docShell) {
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
if (presShell) {
|
|
nsViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->SetFocus(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ActivateOrDeactivateChild(TabParent* aParent, void* aArg)
|
|
{
|
|
bool active = static_cast<bool>(aArg);
|
|
Unused << aParent->SendParentActivated(active);
|
|
}
|
|
|
|
void
|
|
nsFocusManager::ActivateOrDeactivate(nsPIDOMWindow* aWindow, bool aActive)
|
|
{
|
|
if (!aWindow) {
|
|
return;
|
|
}
|
|
|
|
// Inform the DOM window that it has activated or deactivated, so that
|
|
// the active attribute is updated on the window.
|
|
aWindow->ActivateOrDeactivate(aActive);
|
|
|
|
// Send the activate event.
|
|
if (aWindow->GetExtantDoc()) {
|
|
nsContentUtils::DispatchEventOnlyToChrome(aWindow->GetExtantDoc(),
|
|
aWindow,
|
|
aActive ?
|
|
NS_LITERAL_STRING("activate") :
|
|
NS_LITERAL_STRING("deactivate"),
|
|
true, true, nullptr);
|
|
}
|
|
|
|
// Look for any remote child frames, iterate over them and send the activation notification.
|
|
nsContentUtils::CallOnAllRemoteChildren(aWindow, ActivateOrDeactivateChild,
|
|
(void *)aActive);
|
|
}
|
|
|
|
void
|
|
nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
|
|
bool aFocusChanged, bool aAdjustWidget)
|
|
{
|
|
// if the element is not focusable, just return and leave the focus as is
|
|
nsCOMPtr<nsIContent> contentToFocus = CheckIfFocusable(aNewContent, aFlags);
|
|
if (!contentToFocus)
|
|
return;
|
|
|
|
// check if the element to focus is a frame (iframe) containing a child
|
|
// document. Frames are never directly focused; instead focusing a frame
|
|
// means focus what is inside the frame. To do this, the descendant content
|
|
// within the frame is retrieved and that will be focused instead.
|
|
nsCOMPtr<nsPIDOMWindow> newWindow;
|
|
nsCOMPtr<nsPIDOMWindow> subWindow = GetContentWindow(contentToFocus);
|
|
if (subWindow) {
|
|
contentToFocus = GetFocusedDescendant(subWindow, true, getter_AddRefs(newWindow));
|
|
// since a window is being refocused, clear aFocusChanged so that the
|
|
// caret position isn't updated.
|
|
aFocusChanged = false;
|
|
}
|
|
|
|
// unless it was set above, retrieve the window for the element to focus
|
|
if (!newWindow)
|
|
newWindow = GetCurrentWindow(contentToFocus);
|
|
|
|
// if the element is already focused, just return. Note that this happens
|
|
// after the frame check above so that we compare the element that will be
|
|
// focused rather than the frame it is in.
|
|
if (!newWindow || (newWindow == mFocusedWindow && contentToFocus == mFocusedContent))
|
|
return;
|
|
|
|
// don't allow focus to be placed in docshells or descendants of docshells
|
|
// that are being destroyed. Also, ensure that the page hasn't been
|
|
// unloaded. The prevents content from being refocused during an unload event.
|
|
nsCOMPtr<nsIDocShell> newDocShell = newWindow->GetDocShell();
|
|
nsCOMPtr<nsIDocShell> docShell = newDocShell;
|
|
while (docShell) {
|
|
bool inUnload;
|
|
docShell->GetIsInUnload(&inUnload);
|
|
if (inUnload)
|
|
return;
|
|
|
|
bool beingDestroyed;
|
|
docShell->IsBeingDestroyed(&beingDestroyed);
|
|
if (beingDestroyed)
|
|
return;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
docShell->GetParent(getter_AddRefs(parentDsti));
|
|
docShell = do_QueryInterface(parentDsti);
|
|
}
|
|
|
|
// if the new element is in the same window as the currently focused element
|
|
bool isElementInFocusedWindow = (mFocusedWindow == newWindow);
|
|
|
|
if (!isElementInFocusedWindow && mFocusedWindow && newWindow &&
|
|
nsContentUtils::IsHandlingKeyBoardEvent()) {
|
|
nsCOMPtr<nsIScriptObjectPrincipal> focused =
|
|
do_QueryInterface(mFocusedWindow);
|
|
nsCOMPtr<nsIScriptObjectPrincipal> newFocus =
|
|
do_QueryInterface(newWindow);
|
|
nsIPrincipal* focusedPrincipal = focused->GetPrincipal();
|
|
nsIPrincipal* newPrincipal = newFocus->GetPrincipal();
|
|
if (!focusedPrincipal || !newPrincipal) {
|
|
return;
|
|
}
|
|
bool subsumes = false;
|
|
focusedPrincipal->Subsumes(newPrincipal, &subsumes);
|
|
if (!subsumes && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
|
|
NS_WARNING("Not allowed to focus the new window!");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// to check if the new element is in the active window, compare the
|
|
// new root docshell for the new element with the active window's docshell.
|
|
bool isElementInActiveWindow = false;
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = newWindow->GetDocShell();
|
|
nsCOMPtr<nsPIDOMWindow> newRootWindow;
|
|
if (dsti) {
|
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
dsti->GetRootTreeItem(getter_AddRefs(root));
|
|
newRootWindow = root ? root->GetWindow() : nullptr;
|
|
|
|
isElementInActiveWindow = (mActiveWindow && newRootWindow == mActiveWindow);
|
|
}
|
|
|
|
// Exit fullscreen if we're focusing a windowed plugin on a non-MacOSX
|
|
// system. We don't control event dispatch to windowed plugins on non-MacOSX,
|
|
// so we can't display the "Press ESC to leave fullscreen mode" warning on
|
|
// key input if a windowed plugin is focused, so just exit fullscreen
|
|
// to guard against phishing.
|
|
#ifndef XP_MACOSX
|
|
nsIDocument* fullscreenAncestor;
|
|
if (contentToFocus &&
|
|
(fullscreenAncestor = nsContentUtils::GetFullscreenAncestor(contentToFocus->OwnerDoc())) &&
|
|
nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
|
|
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
|
NS_LITERAL_CSTRING("DOM"),
|
|
contentToFocus->OwnerDoc(),
|
|
nsContentUtils::eDOM_PROPERTIES,
|
|
"FocusedWindowedPluginWhileFullScreen");
|
|
nsIDocument::ExitFullscreen(fullscreenAncestor, /* async */ true);
|
|
}
|
|
#endif
|
|
|
|
// if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
|
|
// shifted away from the current element if the new shell to focus is
|
|
// the same or an ancestor shell of the currently focused shell.
|
|
bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
|
|
IsSameOrAncestor(newWindow, mFocusedWindow);
|
|
|
|
// if the element is in the active window, frame switching is allowed and
|
|
// the content is in a visible window, fire blur and focus events.
|
|
bool sendFocusEvent =
|
|
isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow);
|
|
|
|
// When the following conditions are true:
|
|
// * an element has focus
|
|
// * isn't called by trusted event (i.e., called by untrusted event or by js)
|
|
// * the focus is moved to another document's element
|
|
// we need to check the permission.
|
|
if (sendFocusEvent && mFocusedContent && !nsContentUtils::LegacyIsCallerNativeCode() &&
|
|
mFocusedContent->OwnerDoc() != aNewContent->OwnerDoc()) {
|
|
// If the caller cannot access the current focused node, the caller should
|
|
// not be able to steal focus from it. E.g., When the current focused node
|
|
// is in chrome, any web contents should not be able to steal the focus.
|
|
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mFocusedContent));
|
|
sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
|
|
if (!sendFocusEvent && mMouseButtonEventHandlingDocument) {
|
|
// However, while mouse button event is handling, the handling document's
|
|
// script should be able to steal focus.
|
|
domNode = do_QueryInterface(mMouseButtonEventHandlingDocument);
|
|
sendFocusEvent = nsContentUtils::CanCallerAccess(domNode);
|
|
}
|
|
}
|
|
|
|
LOGCONTENT("Shift Focus: %s", contentToFocus.get());
|
|
LOGFOCUS((" Flags: %x Current Window: %p New Window: %p Current Element: %p",
|
|
aFlags, mFocusedWindow.get(), newWindow.get(), mFocusedContent.get()));
|
|
LOGFOCUS((" In Active Window: %d In Focused Window: %d SendFocus: %d",
|
|
isElementInActiveWindow, isElementInFocusedWindow, sendFocusEvent));
|
|
|
|
if (sendFocusEvent) {
|
|
// return if blurring fails or the focus changes during the blur
|
|
if (mFocusedWindow) {
|
|
// if the focus is being moved to another element in the same document,
|
|
// or to a descendant, pass the existing window to Blur so that the
|
|
// current node in the existing window is cleared. If moving to a
|
|
// window elsewhere, we want to maintain the current node in the
|
|
// window but still blur it.
|
|
bool currentIsSameOrAncestor = IsSameOrAncestor(mFocusedWindow, newWindow);
|
|
// find the common ancestor of the currently focused window and the new
|
|
// window. The ancestor will need to have its currently focused node
|
|
// cleared once the document has been blurred. Otherwise, we'll be in a
|
|
// state where a document is blurred yet the chain of windows above it
|
|
// still points to that document.
|
|
// For instance, in the following frame tree:
|
|
// A
|
|
// B C
|
|
// D
|
|
// D is focused and we want to focus C. Once D has been blurred, we need
|
|
// to clear out the focus in A, otherwise A would still maintain that B
|
|
// was focused, and B that D was focused.
|
|
nsCOMPtr<nsPIDOMWindow> commonAncestor;
|
|
if (!isElementInFocusedWindow)
|
|
commonAncestor = GetCommonAncestor(newWindow, mFocusedWindow);
|
|
|
|
if (!Blur(currentIsSameOrAncestor ? mFocusedWindow.get() : nullptr,
|
|
commonAncestor, !isElementInFocusedWindow, aAdjustWidget))
|
|
return;
|
|
}
|
|
|
|
Focus(newWindow, contentToFocus, aFlags, !isElementInFocusedWindow,
|
|
aFocusChanged, false, aAdjustWidget);
|
|
}
|
|
else {
|
|
// otherwise, for inactive windows and when the caller cannot steal the
|
|
// focus, update the node in the window, and raise the window if desired.
|
|
if (allowFrameSwitch)
|
|
AdjustWindowFocus(newWindow, true);
|
|
|
|
// set the focus node and method as needed
|
|
uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
|
|
newWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
|
|
newWindow->SetFocusedNode(contentToFocus, focusMethod);
|
|
if (aFocusChanged) {
|
|
nsCOMPtr<nsIDocShell> docShell = newWindow->GetDocShell();
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
if (presShell)
|
|
ScrollIntoView(presShell, contentToFocus, aFlags);
|
|
}
|
|
|
|
// update the commands even when inactive so that the attributes for that
|
|
// window are up to date.
|
|
if (allowFrameSwitch)
|
|
newWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
|
|
if (aFlags & FLAG_RAISE)
|
|
RaiseWindow(newRootWindow);
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsFocusManager::IsSameOrAncestor(nsPIDOMWindow* aPossibleAncestor,
|
|
nsPIDOMWindow* aWindow)
|
|
{
|
|
if (!aWindow || !aPossibleAncestor) {
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> ancestordsti = aPossibleAncestor->GetDocShell();
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = aWindow->GetDocShell();
|
|
while (dsti) {
|
|
if (dsti == ancestordsti)
|
|
return true;
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
dsti.swap(parentDsti);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
already_AddRefed<nsPIDOMWindow>
|
|
nsFocusManager::GetCommonAncestor(nsPIDOMWindow* aWindow1,
|
|
nsPIDOMWindow* aWindow2)
|
|
{
|
|
NS_ENSURE_TRUE(aWindow1 && aWindow2, nullptr);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti1 = aWindow1->GetDocShell();
|
|
NS_ENSURE_TRUE(dsti1, nullptr);
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti2 = aWindow2->GetDocShell();
|
|
NS_ENSURE_TRUE(dsti2, nullptr);
|
|
|
|
AutoTArray<nsIDocShellTreeItem*, 30> parents1, parents2;
|
|
do {
|
|
parents1.AppendElement(dsti1);
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti1;
|
|
dsti1->GetParent(getter_AddRefs(parentDsti1));
|
|
dsti1.swap(parentDsti1);
|
|
} while (dsti1);
|
|
do {
|
|
parents2.AppendElement(dsti2);
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti2;
|
|
dsti2->GetParent(getter_AddRefs(parentDsti2));
|
|
dsti2.swap(parentDsti2);
|
|
} while (dsti2);
|
|
|
|
uint32_t pos1 = parents1.Length();
|
|
uint32_t pos2 = parents2.Length();
|
|
nsIDocShellTreeItem* parent = nullptr;
|
|
uint32_t len;
|
|
for (len = std::min(pos1, pos2); len > 0; --len) {
|
|
nsIDocShellTreeItem* child1 = parents1.ElementAt(--pos1);
|
|
nsIDocShellTreeItem* child2 = parents2.ElementAt(--pos2);
|
|
if (child1 != child2) {
|
|
break;
|
|
}
|
|
parent = child1;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = parent ? parent->GetWindow() : nullptr;
|
|
return window.forget();
|
|
}
|
|
|
|
void
|
|
nsFocusManager::AdjustWindowFocus(nsPIDOMWindow* aWindow,
|
|
bool aCheckPermission)
|
|
{
|
|
bool isVisible = IsWindowVisible(aWindow);
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window(aWindow);
|
|
while (window) {
|
|
// get the containing <iframe> or equivalent element so that it can be
|
|
// focused below.
|
|
nsCOMPtr<Element> frameElement = window->GetFrameElementInternal();
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = window->GetDocShell();
|
|
if (!dsti)
|
|
return;
|
|
nsCOMPtr<nsIDocShellTreeItem> parentDsti;
|
|
dsti->GetParent(getter_AddRefs(parentDsti));
|
|
if (!parentDsti) {
|
|
return;
|
|
}
|
|
|
|
window = parentDsti->GetWindow();
|
|
if (window) {
|
|
// if the parent window is visible but aWindow was not, then we have
|
|
// likely moved up and out from a hidden tab to the browser window, or a
|
|
// similar such arrangement. Stop adjusting the current nodes.
|
|
if (IsWindowVisible(window) != isVisible)
|
|
break;
|
|
|
|
// When aCheckPermission is true, we should check whether the caller can
|
|
// access the window or not. If it cannot access, we should stop the
|
|
// adjusting.
|
|
if (aCheckPermission && !nsContentUtils::LegacyIsCallerNativeCode() &&
|
|
!nsContentUtils::CanCallerAccess(window)) {
|
|
break;
|
|
}
|
|
|
|
window->SetFocusedNode(frameElement);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool
|
|
nsFocusManager::IsWindowVisible(nsPIDOMWindow* aWindow)
|
|
{
|
|
if (!aWindow || aWindow->IsFrozen())
|
|
return false;
|
|
|
|
// Check if the inner window is frozen as well. This can happen when a focus change
|
|
// occurs while restoring a previous page.
|
|
nsPIDOMWindow* innerWindow = aWindow->GetCurrentInnerWindow();
|
|
if (!innerWindow || innerWindow->IsFrozen())
|
|
return false;
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(docShell));
|
|
if (!baseWin)
|
|
return false;
|
|
|
|
bool visible = false;
|
|
baseWin->GetVisibility(&visible);
|
|
return visible;
|
|
}
|
|
|
|
bool
|
|
nsFocusManager::IsNonFocusableRoot(nsIContent* aContent)
|
|
{
|
|
NS_PRECONDITION(aContent, "aContent must not be NULL");
|
|
NS_PRECONDITION(aContent->IsInComposedDoc(), "aContent must be in a document");
|
|
|
|
// If aContent is in designMode, the root element is not focusable.
|
|
// NOTE: in designMode, most elements are not focusable, just the document is
|
|
// focusable.
|
|
// Also, if aContent is not editable but it isn't in designMode, it's not
|
|
// focusable.
|
|
// And in userfocusignored context nothing is focusable.
|
|
nsIDocument* doc = aContent->GetComposedDoc();
|
|
NS_ASSERTION(doc, "aContent must have current document");
|
|
return aContent == doc->GetRootElement() &&
|
|
(doc->HasFlag(NODE_IS_EDITABLE) || !aContent->IsEditable() ||
|
|
nsContentUtils::IsUserFocusIgnored(aContent));
|
|
}
|
|
|
|
nsIContent*
|
|
nsFocusManager::CheckIfFocusable(nsIContent* aContent, uint32_t aFlags)
|
|
{
|
|
if (!aContent)
|
|
return nullptr;
|
|
|
|
// this is a special case for some XUL elements where an anonymous child is
|
|
// actually focusable and not the element itself.
|
|
nsCOMPtr<nsIContent> redirectedFocus = GetRedirectedFocus(aContent);
|
|
if (redirectedFocus)
|
|
return CheckIfFocusable(redirectedFocus, aFlags);
|
|
|
|
nsCOMPtr<nsIDocument> doc = aContent->GetComposedDoc();
|
|
// can't focus elements that are not in documents
|
|
if (!doc) {
|
|
LOGCONTENT("Cannot focus %s because content not in document", aContent)
|
|
return nullptr;
|
|
}
|
|
|
|
// Make sure that our frames are up to date
|
|
doc->FlushPendingNotifications(Flush_Layout);
|
|
|
|
nsIPresShell *shell = doc->GetShell();
|
|
if (!shell)
|
|
return nullptr;
|
|
|
|
// the root content can always be focused,
|
|
// except in userfocusignored context.
|
|
if (aContent == doc->GetRootElement())
|
|
return nsContentUtils::IsUserFocusIgnored(aContent) ? nullptr : aContent;
|
|
|
|
// cannot focus content in print preview mode. Only the root can be focused.
|
|
nsPresContext* presContext = shell->GetPresContext();
|
|
if (presContext && presContext->Type() == nsPresContext::eContext_PrintPreview) {
|
|
LOGCONTENT("Cannot focus %s while in print preview", aContent)
|
|
return nullptr;
|
|
}
|
|
|
|
nsIFrame* frame = aContent->GetPrimaryFrame();
|
|
if (!frame) {
|
|
LOGCONTENT("Cannot focus %s as it has no frame", aContent)
|
|
return nullptr;
|
|
}
|
|
|
|
if (aContent->IsHTMLElement(nsGkAtoms::area)) {
|
|
// HTML areas do not have their own frame, and the img frame we get from
|
|
// GetPrimaryFrame() is not relevant as to whether it is focusable or
|
|
// not, so we have to do all the relevant checks manually for them.
|
|
return frame->IsVisibleConsideringAncestors() &&
|
|
aContent->IsFocusable() ? aContent : nullptr;
|
|
}
|
|
|
|
// if this is a child frame content node, check if it is visible and
|
|
// call the content node's IsFocusable method instead of the frame's
|
|
// IsFocusable method. This skips checking the style system and ensures that
|
|
// offscreen browsers can still be focused.
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
|
if (subdoc && IsWindowVisible(subdoc->GetWindow())) {
|
|
const nsStyleUserInterface* ui = frame->StyleUserInterface();
|
|
int32_t tabIndex = (ui->mUserFocus == NS_STYLE_USER_FOCUS_IGNORE ||
|
|
ui->mUserFocus == NS_STYLE_USER_FOCUS_NONE) ? -1 : 0;
|
|
return aContent->IsFocusable(&tabIndex, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
|
|
}
|
|
|
|
return frame->IsFocusable(nullptr, aFlags & FLAG_BYMOUSE) ? aContent : nullptr;
|
|
}
|
|
|
|
bool
|
|
nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
|
nsPIDOMWindow* aAncestorWindowToFocus,
|
|
bool aIsLeavingDocument,
|
|
bool aAdjustWidgets)
|
|
{
|
|
LOGFOCUS(("<<Blur begin>>"));
|
|
|
|
// hold a reference to the focused content, which may be null
|
|
nsCOMPtr<nsIContent> content = mFocusedContent;
|
|
if (content) {
|
|
if (!content->IsInComposedDoc()) {
|
|
mFocusedContent = nullptr;
|
|
return true;
|
|
}
|
|
if (content == mFirstBlurEvent)
|
|
return true;
|
|
}
|
|
|
|
// hold a reference to the focused window
|
|
nsCOMPtr<nsPIDOMWindow> window = mFocusedWindow;
|
|
if (!window) {
|
|
mFocusedContent = nullptr;
|
|
return true;
|
|
}
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
|
if (!docShell) {
|
|
mFocusedContent = nullptr;
|
|
return true;
|
|
}
|
|
|
|
// Keep a ref to presShell since dispatching the DOM event may cause
|
|
// the document to be destroyed.
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
if (!presShell) {
|
|
mFocusedContent = nullptr;
|
|
return true;
|
|
}
|
|
|
|
bool clearFirstBlurEvent = false;
|
|
if (!mFirstBlurEvent) {
|
|
mFirstBlurEvent = content;
|
|
clearFirstBlurEvent = true;
|
|
}
|
|
|
|
nsPresContext* focusedPresContext =
|
|
mActiveWindow ? presShell->GetPresContext() : nullptr;
|
|
IMEStateManager::OnChangeFocus(focusedPresContext, nullptr,
|
|
GetFocusMoveActionCause(0));
|
|
|
|
// now adjust the actual focus, by clearing the fields in the focus manager
|
|
// and in the window.
|
|
mFocusedContent = nullptr;
|
|
bool shouldShowFocusRing = window->ShouldShowFocusRing();
|
|
if (aWindowToClear)
|
|
aWindowToClear->SetFocusedNode(nullptr);
|
|
|
|
LOGCONTENT("Element %s has been blurred", content.get());
|
|
|
|
// Don't fire blur event on the root content which isn't editable.
|
|
bool sendBlurEvent =
|
|
content && content->IsInComposedDoc() && !IsNonFocusableRoot(content);
|
|
if (content) {
|
|
if (sendBlurEvent) {
|
|
NotifyFocusStateChange(content, shouldShowFocusRing, false);
|
|
}
|
|
|
|
// if an object/plug-in/remote browser is being blurred, move the system focus
|
|
// to the parent window, otherwise events will still get fired at the plugin.
|
|
// But don't do this if we are blurring due to the window being lowered,
|
|
// otherwise, the parent window can get raised again.
|
|
if (mActiveWindow) {
|
|
nsIFrame* contentFrame = content->GetPrimaryFrame();
|
|
nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
|
|
if (aAdjustWidgets && objectFrame && !sTestMode) {
|
|
if (XRE_IsContentProcess()) {
|
|
// set focus to the top level window via the chrome process.
|
|
nsCOMPtr<nsITabChild> tabChild = do_GetInterface(docShell);
|
|
if (tabChild) {
|
|
static_cast<TabChild*>(tabChild.get())->SendDispatchFocusToTopLevelWindow();
|
|
}
|
|
} else {
|
|
// note that the presshell's widget is being retrieved here, not the one
|
|
// for the object frame.
|
|
nsViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
if (widget) {
|
|
// set focus to the top level window but don't raise it.
|
|
widget->SetFocus(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// if the object being blurred is a remote browser, deactivate remote content
|
|
if (TabParent* remote = TabParent::GetFrom(content)) {
|
|
remote->Deactivate();
|
|
LOGFOCUS(("Remote browser deactivated"));
|
|
}
|
|
}
|
|
|
|
bool result = true;
|
|
if (sendBlurEvent) {
|
|
// if there is an active window, update commands. If there isn't an active
|
|
// window, then this was a blur caused by the active window being lowered,
|
|
// so there is no need to update the commands
|
|
if (mActiveWindow)
|
|
window->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
|
|
SendFocusOrBlurEvent(eBlur, presShell,
|
|
content->GetComposedDoc(), content, 1, false);
|
|
}
|
|
|
|
// if we are leaving the document or the window was lowered, make the caret
|
|
// invisible.
|
|
if (aIsLeavingDocument || !mActiveWindow) {
|
|
SetCaretVisible(presShell, false, nullptr);
|
|
}
|
|
|
|
RefPtr<AccessibleCaretEventHub> eventHub = presShell->GetAccessibleCaretEventHub();
|
|
if (eventHub) {
|
|
eventHub->NotifyBlur(aIsLeavingDocument || !mActiveWindow);
|
|
}
|
|
|
|
// at this point, it is expected that this window will be still be
|
|
// focused, but the focused content will be null, as it was cleared before
|
|
// the event. If this isn't the case, then something else was focused during
|
|
// the blur event above and we should just return. However, if
|
|
// aIsLeavingDocument is set, a new document is desired, so make sure to
|
|
// blur the document and window.
|
|
if (mFocusedWindow != window ||
|
|
(mFocusedContent != nullptr && !aIsLeavingDocument)) {
|
|
result = false;
|
|
}
|
|
else if (aIsLeavingDocument) {
|
|
window->TakeFocus(false, 0);
|
|
|
|
// clear the focus so that the ancestor frame hierarchy is in the correct
|
|
// state. Pass true because aAncestorWindowToFocus is thought to be
|
|
// focused at this point.
|
|
if (aAncestorWindowToFocus)
|
|
aAncestorWindowToFocus->SetFocusedNode(nullptr, 0, true);
|
|
|
|
SetFocusedWindowInternal(nullptr);
|
|
mFocusedContent = nullptr;
|
|
|
|
// pass 1 for the focus method when calling SendFocusOrBlurEvent just so
|
|
// that the check is made for suppressed documents. Check to ensure that
|
|
// the document isn't null in case someone closed it during the blur above
|
|
nsIDocument* doc = window->GetExtantDoc();
|
|
if (doc)
|
|
SendFocusOrBlurEvent(eBlur, presShell, doc, doc, 1, false);
|
|
if (mFocusedWindow == nullptr)
|
|
SendFocusOrBlurEvent(eBlur, presShell, doc, window, 1, false);
|
|
|
|
// check if a different window was focused
|
|
result = (mFocusedWindow == nullptr && mActiveWindow);
|
|
}
|
|
else if (mActiveWindow) {
|
|
// Otherwise, the blur of the element without blurring the document
|
|
// occurred normally. Call UpdateCaret to redisplay the caret at the right
|
|
// location within the document. This is needed to ensure that the caret
|
|
// used for caret browsing is made visible again when an input field is
|
|
// blurred.
|
|
UpdateCaret(false, true, nullptr);
|
|
}
|
|
|
|
if (clearFirstBlurEvent)
|
|
mFirstBlurEvent = nullptr;
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsFocusManager::Focus(nsPIDOMWindow* aWindow,
|
|
nsIContent* aContent,
|
|
uint32_t aFlags,
|
|
bool aIsNewDocument,
|
|
bool aFocusChanged,
|
|
bool aWindowRaised,
|
|
bool aAdjustWidgets)
|
|
{
|
|
LOGFOCUS(("<<Focus begin>>"));
|
|
|
|
if (!aWindow)
|
|
return;
|
|
|
|
if (aContent && (aContent == mFirstFocusEvent || aContent == mFirstBlurEvent))
|
|
return;
|
|
|
|
// Keep a reference to the presShell since dispatching the DOM event may
|
|
// cause the document to be destroyed.
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
if (!docShell)
|
|
return;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
if (!presShell)
|
|
return;
|
|
|
|
// If the focus actually changed, set the focus method (mouse, keyboard, etc).
|
|
// Otherwise, just get the current focus method and use that. This ensures
|
|
// that the method is set during the document and window focus events.
|
|
uint32_t focusMethod = aFocusChanged ? aFlags & FOCUSMETHODANDRING_MASK :
|
|
aWindow->GetFocusMethod() | (aFlags & FLAG_SHOWRING);
|
|
|
|
if (!IsWindowVisible(aWindow)) {
|
|
// if the window isn't visible, for instance because it is a hidden tab,
|
|
// update the current focus and scroll it into view but don't do anything else
|
|
if (CheckIfFocusable(aContent, aFlags)) {
|
|
aWindow->SetFocusedNode(aContent, focusMethod);
|
|
if (aFocusChanged)
|
|
ScrollIntoView(presShell, aContent, aFlags);
|
|
}
|
|
return;
|
|
}
|
|
|
|
bool clearFirstFocusEvent = false;
|
|
if (!mFirstFocusEvent) {
|
|
mFirstFocusEvent = aContent;
|
|
clearFirstFocusEvent = true;
|
|
}
|
|
|
|
LOGCONTENT("Element %s has been focused", aContent);
|
|
|
|
if (MOZ_LOG_TEST(gFocusLog, LogLevel::Debug)) {
|
|
nsIDocument* docm = aWindow->GetExtantDoc();
|
|
if (docm) {
|
|
LOGCONTENT(" from %s", docm->GetRootElement());
|
|
}
|
|
LOGFOCUS((" [Newdoc: %d FocusChanged: %d Raised: %d Flags: %x]",
|
|
aIsNewDocument, aFocusChanged, aWindowRaised, aFlags));
|
|
}
|
|
|
|
if (aIsNewDocument) {
|
|
// if this is a new document, update the parent chain of frames so that
|
|
// focus can be traversed from the top level down to the newly focused
|
|
// window.
|
|
AdjustWindowFocus(aWindow, false);
|
|
}
|
|
|
|
// indicate that the window has taken focus.
|
|
if (aWindow->TakeFocus(true, focusMethod))
|
|
aIsNewDocument = true;
|
|
|
|
SetFocusedWindowInternal(aWindow);
|
|
|
|
// Update the system focus by focusing the root widget. But avoid this
|
|
// if 1) aAdjustWidgets is false or 2) aContent is a plugin that has its
|
|
// own widget and is either already focused or is about to be focused.
|
|
nsCOMPtr<nsIWidget> objectFrameWidget;
|
|
if (aContent) {
|
|
nsIFrame* contentFrame = aContent->GetPrimaryFrame();
|
|
nsIObjectFrame* objectFrame = do_QueryFrame(contentFrame);
|
|
if (objectFrame)
|
|
objectFrameWidget = objectFrame->GetWidget();
|
|
}
|
|
if (aAdjustWidgets && !objectFrameWidget && !sTestMode) {
|
|
nsViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->SetFocus(false);
|
|
}
|
|
}
|
|
|
|
// if switching to a new document, first fire the focus event on the
|
|
// document and then the window.
|
|
if (aIsNewDocument) {
|
|
nsIDocument* doc = aWindow->GetExtantDoc();
|
|
// The focus change should be notified to IMEStateManager from here if
|
|
// the focused content is a designMode editor since any content won't
|
|
// receive focus event.
|
|
if (doc && doc->HasFlag(NODE_IS_EDITABLE)) {
|
|
IMEStateManager::OnChangeFocus(presShell->GetPresContext(), nullptr,
|
|
GetFocusMoveActionCause(aFlags));
|
|
}
|
|
if (doc)
|
|
SendFocusOrBlurEvent(eFocus, presShell, doc,
|
|
doc, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
|
|
if (mFocusedWindow == aWindow && mFocusedContent == nullptr)
|
|
SendFocusOrBlurEvent(eFocus, presShell, doc,
|
|
aWindow, aFlags & FOCUSMETHOD_MASK, aWindowRaised);
|
|
}
|
|
|
|
// check to ensure that the element is still focusable, and that nothing
|
|
// else was focused during the events above.
|
|
if (CheckIfFocusable(aContent, aFlags) &&
|
|
mFocusedWindow == aWindow && mFocusedContent == nullptr) {
|
|
mFocusedContent = aContent;
|
|
|
|
nsIContent* focusedNode = aWindow->GetFocusedNode();
|
|
bool isRefocus = focusedNode && focusedNode->IsEqualNode(aContent);
|
|
|
|
aWindow->SetFocusedNode(aContent, focusMethod);
|
|
|
|
bool sendFocusEvent =
|
|
aContent && aContent->IsInComposedDoc() && !IsNonFocusableRoot(aContent);
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
if (sendFocusEvent) {
|
|
// if the focused element changed, scroll it into view
|
|
if (aFocusChanged)
|
|
ScrollIntoView(presShell, aContent, aFlags);
|
|
|
|
NotifyFocusStateChange(aContent, aWindow->ShouldShowFocusRing(), true);
|
|
|
|
// if this is an object/plug-in/remote browser, focus its widget. Note that we might
|
|
// no longer be in the same document, due to the events we fired above when
|
|
// aIsNewDocument.
|
|
if (presShell->GetDocument() == aContent->GetComposedDoc()) {
|
|
if (aAdjustWidgets && objectFrameWidget && !sTestMode)
|
|
objectFrameWidget->SetFocus(false);
|
|
|
|
// if the object being focused is a remote browser, activate remote content
|
|
if (TabParent* remote = TabParent::GetFrom(aContent)) {
|
|
remote->Activate();
|
|
LOGFOCUS(("Remote browser activated"));
|
|
}
|
|
}
|
|
|
|
IMEStateManager::OnChangeFocus(presContext, aContent,
|
|
GetFocusMoveActionCause(aFlags));
|
|
|
|
// as long as this focus wasn't because a window was raised, update the
|
|
// commands
|
|
// XXXndeakin P2 someone could adjust the focus during the update
|
|
if (!aWindowRaised)
|
|
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
|
|
SendFocusOrBlurEvent(eFocus, presShell,
|
|
aContent->GetComposedDoc(),
|
|
aContent, aFlags & FOCUSMETHOD_MASK,
|
|
aWindowRaised, isRefocus);
|
|
} else {
|
|
IMEStateManager::OnChangeFocus(presContext, nullptr,
|
|
GetFocusMoveActionCause(aFlags));
|
|
if (!aWindowRaised) {
|
|
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// If the window focus event (fired above when aIsNewDocument) caused
|
|
// the plugin not to be focusable, update the system focus by focusing
|
|
// the root widget.
|
|
if (aAdjustWidgets && objectFrameWidget &&
|
|
mFocusedWindow == aWindow && mFocusedContent == nullptr &&
|
|
!sTestMode) {
|
|
nsViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->SetFocus(false);
|
|
}
|
|
}
|
|
|
|
if (!mFocusedContent) {
|
|
// When there is no focused content, IMEStateManager needs to adjust IME
|
|
// enabled state with the document.
|
|
nsPresContext* presContext = presShell->GetPresContext();
|
|
IMEStateManager::OnChangeFocus(presContext, nullptr,
|
|
GetFocusMoveActionCause(aFlags));
|
|
}
|
|
|
|
if (!aWindowRaised)
|
|
aWindow->UpdateCommands(NS_LITERAL_STRING("focus"), nullptr, 0);
|
|
}
|
|
|
|
// update the caret visibility and position to match the newly focused
|
|
// element. However, don't update the position if this was a focus due to a
|
|
// mouse click as the selection code would already have moved the caret as
|
|
// needed. If this is a different document than was focused before, also
|
|
// update the caret's visibility. If this is the same document, the caret
|
|
// visibility should be the same as before so there is no need to update it.
|
|
if (mFocusedContent == aContent)
|
|
UpdateCaret(aFocusChanged && !(aFlags & FLAG_BYMOUSE), aIsNewDocument,
|
|
mFocusedContent);
|
|
|
|
if (clearFirstFocusEvent)
|
|
mFirstFocusEvent = nullptr;
|
|
}
|
|
|
|
class FocusBlurEvent : public nsRunnable
|
|
{
|
|
public:
|
|
FocusBlurEvent(nsISupports* aTarget, EventMessage aEventMessage,
|
|
nsPresContext* aContext, bool aWindowRaised,
|
|
bool aIsRefocus)
|
|
: mTarget(aTarget)
|
|
, mContext(aContext)
|
|
, mEventMessage(aEventMessage)
|
|
, mWindowRaised(aWindowRaised)
|
|
, mIsRefocus(aIsRefocus)
|
|
{
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
InternalFocusEvent event(true, mEventMessage);
|
|
event.mFlags.mBubbles = false;
|
|
event.mFlags.mCancelable = false;
|
|
event.mFromRaise = mWindowRaised;
|
|
event.mIsRefocus = mIsRefocus;
|
|
return EventDispatcher::Dispatch(mTarget, mContext, &event);
|
|
}
|
|
|
|
nsCOMPtr<nsISupports> mTarget;
|
|
RefPtr<nsPresContext> mContext;
|
|
EventMessage mEventMessage;
|
|
bool mWindowRaised;
|
|
bool mIsRefocus;
|
|
};
|
|
|
|
void
|
|
nsFocusManager::SendFocusOrBlurEvent(EventMessage aEventMessage,
|
|
nsIPresShell* aPresShell,
|
|
nsIDocument* aDocument,
|
|
nsISupports* aTarget,
|
|
uint32_t aFocusMethod,
|
|
bool aWindowRaised,
|
|
bool aIsRefocus)
|
|
{
|
|
NS_ASSERTION(aEventMessage == eFocus || aEventMessage == eBlur,
|
|
"Wrong event type for SendFocusOrBlurEvent");
|
|
|
|
nsCOMPtr<EventTarget> eventTarget = do_QueryInterface(aTarget);
|
|
|
|
nsCOMPtr<nsINode> n = do_QueryInterface(aTarget);
|
|
if (!n) {
|
|
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aTarget);
|
|
n = win ? win->GetExtantDoc() : nullptr;
|
|
}
|
|
bool dontDispatchEvent = n && nsContentUtils::IsUserFocusIgnored(n);
|
|
|
|
// for focus events, if this event was from a mouse or key and event
|
|
// handling on the document is suppressed, queue the event and fire it
|
|
// later. For blur events, a non-zero value would be set for aFocusMethod.
|
|
if (aFocusMethod && !dontDispatchEvent &&
|
|
aDocument && aDocument->EventHandlingSuppressed()) {
|
|
// aFlags is always 0 when aWindowRaised is true so this won't be called
|
|
// on a window raise.
|
|
NS_ASSERTION(!aWindowRaised, "aWindowRaised should not be set");
|
|
|
|
for (uint32_t i = mDelayedBlurFocusEvents.Length(); i > 0; --i) {
|
|
// if this event was already queued, remove it and append it to the end
|
|
if (mDelayedBlurFocusEvents[i - 1].mEventMessage == aEventMessage &&
|
|
mDelayedBlurFocusEvents[i - 1].mPresShell == aPresShell &&
|
|
mDelayedBlurFocusEvents[i - 1].mDocument == aDocument &&
|
|
mDelayedBlurFocusEvents[i - 1].mTarget == eventTarget) {
|
|
mDelayedBlurFocusEvents.RemoveElementAt(i - 1);
|
|
}
|
|
}
|
|
|
|
mDelayedBlurFocusEvents.AppendElement(
|
|
nsDelayedBlurOrFocusEvent(aEventMessage, aPresShell,
|
|
aDocument, eventTarget));
|
|
return;
|
|
}
|
|
|
|
#ifdef ACCESSIBILITY
|
|
nsAccessibilityService* accService = GetAccService();
|
|
if (accService) {
|
|
if (aEventMessage == eFocus) {
|
|
accService->NotifyOfDOMFocus(aTarget);
|
|
} else {
|
|
accService->NotifyOfDOMBlur(aTarget);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!dontDispatchEvent) {
|
|
nsContentUtils::AddScriptRunner(
|
|
new FocusBlurEvent(aTarget, aEventMessage, aPresShell->GetPresContext(),
|
|
aWindowRaised, aIsRefocus));
|
|
}
|
|
}
|
|
|
|
void
|
|
nsFocusManager::ScrollIntoView(nsIPresShell* aPresShell,
|
|
nsIContent* aContent,
|
|
uint32_t aFlags)
|
|
{
|
|
// if the noscroll flag isn't set, scroll the newly focused element into view
|
|
if (!(aFlags & FLAG_NOSCROLL))
|
|
aPresShell->ScrollContentIntoView(aContent,
|
|
nsIPresShell::ScrollAxis(
|
|
nsIPresShell::SCROLL_MINIMUM,
|
|
nsIPresShell::SCROLL_IF_NOT_VISIBLE),
|
|
nsIPresShell::ScrollAxis(
|
|
nsIPresShell::SCROLL_MINIMUM,
|
|
nsIPresShell::SCROLL_IF_NOT_VISIBLE),
|
|
nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
|
|
}
|
|
|
|
|
|
void
|
|
nsFocusManager::RaiseWindow(nsPIDOMWindow* aWindow)
|
|
{
|
|
// don't raise windows that are already raised or are in the process of
|
|
// being lowered
|
|
if (!aWindow || aWindow == mActiveWindow || aWindow == mWindowBeingLowered)
|
|
return;
|
|
|
|
if (sTestMode) {
|
|
// In test mode, emulate the existing window being lowered and the new
|
|
// window being raised.
|
|
if (mActiveWindow)
|
|
WindowLowered(mActiveWindow);
|
|
WindowRaised(aWindow);
|
|
return;
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
// Windows would rather we focus the child widget, otherwise, the toplevel
|
|
// widget will always end up being focused. Fortunately, focusing the child
|
|
// widget will also have the effect of raising the window this widget is in.
|
|
// But on other platforms, we can just focus the toplevel widget to raise
|
|
// the window.
|
|
nsCOMPtr<nsPIDOMWindow> childWindow;
|
|
GetFocusedDescendant(aWindow, true, getter_AddRefs(childWindow));
|
|
if (!childWindow)
|
|
childWindow = aWindow;
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
if (!docShell)
|
|
return;
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
|
|
if (!presShell)
|
|
return;
|
|
|
|
nsViewManager* vm = presShell->GetViewManager();
|
|
if (vm) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
vm->GetRootWidget(getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->SetFocus(true);
|
|
}
|
|
#else
|
|
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin =
|
|
do_QueryInterface(aWindow->GetDocShell());
|
|
if (treeOwnerAsWin) {
|
|
nsCOMPtr<nsIWidget> widget;
|
|
treeOwnerAsWin->GetMainWidget(getter_AddRefs(widget));
|
|
if (widget)
|
|
widget->SetFocus(true);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
nsFocusManager::UpdateCaretForCaretBrowsingMode()
|
|
{
|
|
UpdateCaret(false, true, mFocusedContent);
|
|
}
|
|
|
|
void
|
|
nsFocusManager::UpdateCaret(bool aMoveCaretToFocus,
|
|
bool aUpdateVisibility,
|
|
nsIContent* aContent)
|
|
{
|
|
LOGFOCUS(("Update Caret: %d %d", aMoveCaretToFocus, aUpdateVisibility));
|
|
|
|
if (!mFocusedWindow)
|
|
return;
|
|
|
|
// this is called when a document is focused or when the caretbrowsing
|
|
// preference is changed
|
|
nsCOMPtr<nsIDocShell> focusedDocShell = mFocusedWindow->GetDocShell();
|
|
nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(focusedDocShell);
|
|
if (!dsti)
|
|
return;
|
|
|
|
if (dsti->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
|
return; // Never browse with caret in chrome
|
|
}
|
|
|
|
bool browseWithCaret =
|
|
Preferences::GetBool("accessibility.browsewithcaret");
|
|
|
|
nsCOMPtr<nsIPresShell> presShell = focusedDocShell->GetPresShell();
|
|
if (!presShell)
|
|
return;
|
|
|
|
// If this is an editable document which isn't contentEditable, or a
|
|
// contentEditable document and the node to focus is contentEditable,
|
|
// return, so that we don't mess with caret visibility.
|
|
bool isEditable = false;
|
|
focusedDocShell->GetEditable(&isEditable);
|
|
|
|
if (isEditable) {
|
|
nsCOMPtr<nsIHTMLDocument> doc =
|
|
do_QueryInterface(presShell->GetDocument());
|
|
|
|
bool isContentEditableDoc =
|
|
doc && doc->GetEditingState() == nsIHTMLDocument::eContentEditable;
|
|
|
|
bool isFocusEditable =
|
|
aContent && aContent->HasFlag(NODE_IS_EDITABLE);
|
|
if (!isContentEditableDoc || isFocusEditable)
|
|
return;
|
|
}
|
|
|
|
if (!isEditable && aMoveCaretToFocus)
|
|
MoveCaretToFocus(presShell, aContent);
|
|
|
|
if (!aUpdateVisibility)
|
|
return;
|
|
|
|
// XXXndeakin this doesn't seem right. It should be checking for this only
|
|
// on the nearest ancestor frame which is a chrome frame. But this is
|
|
// what the existing code does, so just leave it for now.
|
|
if (!browseWithCaret) {
|
|
nsCOMPtr<Element> docElement =
|
|
mFocusedWindow->GetFrameElementInternal();
|
|
if (docElement)
|
|
browseWithCaret = docElement->AttrValueIs(kNameSpaceID_None,
|
|
nsGkAtoms::showcaret,
|
|
NS_LITERAL_STRING("true"),
|
|
eCaseMatters);
|
|
}
|
|
|
|
SetCaretVisible(presShell, browseWithCaret, aContent);
|
|
}
|
|
|
|
void
|
|
nsFocusManager::MoveCaretToFocus(nsIPresShell* aPresShell, nsIContent* aContent)
|
|
{
|
|
// domDoc is a document interface we can create a range with
|
|
nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aPresShell->GetDocument());
|
|
if (domDoc) {
|
|
RefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
|
|
nsCOMPtr<nsISelection> domSelection = frameSelection->
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
if (domSelection) {
|
|
nsCOMPtr<nsIDOMNode> currentFocusNode(do_QueryInterface(aContent));
|
|
// First clear the selection. This way, if there is no currently focused
|
|
// content, the selection will just be cleared.
|
|
domSelection->RemoveAllRanges();
|
|
if (currentFocusNode) {
|
|
nsCOMPtr<nsIDOMRange> newRange;
|
|
nsresult rv = domDoc->CreateRange(getter_AddRefs(newRange));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
// Set the range to the start of the currently focused node
|
|
// Make sure it's collapsed
|
|
newRange->SelectNodeContents(currentFocusNode);
|
|
nsCOMPtr<nsIDOMNode> firstChild;
|
|
currentFocusNode->GetFirstChild(getter_AddRefs(firstChild));
|
|
if (!firstChild ||
|
|
aContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL)) {
|
|
// If current focus node is a leaf, set range to before the
|
|
// node by using the parent as a container.
|
|
// This prevents it from appearing as selected.
|
|
newRange->SetStartBefore(currentFocusNode);
|
|
newRange->SetEndBefore(currentFocusNode);
|
|
}
|
|
domSelection->AddRange(newRange);
|
|
domSelection->CollapseToStart();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::SetCaretVisible(nsIPresShell* aPresShell,
|
|
bool aVisible,
|
|
nsIContent* aContent)
|
|
{
|
|
// When browsing with caret, make sure caret is visible after new focus
|
|
// Return early if there is no caret. This can happen for the testcase
|
|
// for bug 308025 where a window is closed in a blur handler.
|
|
RefPtr<nsCaret> caret = aPresShell->GetCaret();
|
|
if (!caret)
|
|
return NS_OK;
|
|
|
|
bool caretVisible = caret->IsVisible();
|
|
if (!aVisible && !caretVisible)
|
|
return NS_OK;
|
|
|
|
RefPtr<nsFrameSelection> frameSelection;
|
|
if (aContent) {
|
|
NS_ASSERTION(aContent->GetComposedDoc() == aPresShell->GetDocument(),
|
|
"Wrong document?");
|
|
nsIFrame *focusFrame = aContent->GetPrimaryFrame();
|
|
if (focusFrame)
|
|
frameSelection = focusFrame->GetFrameSelection();
|
|
}
|
|
|
|
RefPtr<nsFrameSelection> docFrameSelection = aPresShell->FrameSelection();
|
|
|
|
if (docFrameSelection && caret &&
|
|
(frameSelection == docFrameSelection || !aContent)) {
|
|
nsISelection* domSelection = docFrameSelection->
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
if (domSelection) {
|
|
nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(aPresShell));
|
|
if (!selCon) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
// First, hide the caret to prevent attempting to show it in SetCaretDOMSelection
|
|
selCon->SetCaretEnabled(false);
|
|
|
|
// Caret must blink on non-editable elements
|
|
caret->SetIgnoreUserModify(true);
|
|
// Tell the caret which selection to use
|
|
caret->SetSelection(domSelection);
|
|
|
|
// In content, we need to set the caret. The only special case is edit
|
|
// fields, which have a different frame selection from the document.
|
|
// They will take care of making the caret visible themselves.
|
|
|
|
selCon->SetCaretReadOnly(false);
|
|
selCon->SetCaretEnabled(aVisible);
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::GetSelectionLocation(nsIDocument* aDocument,
|
|
nsIPresShell* aPresShell,
|
|
nsIContent **aStartContent,
|
|
nsIContent **aEndContent)
|
|
{
|
|
*aStartContent = *aEndContent = nullptr;
|
|
nsresult rv = NS_ERROR_FAILURE;
|
|
|
|
nsPresContext* presContext = aPresShell->GetPresContext();
|
|
NS_ASSERTION(presContext, "mPresContent is null!!");
|
|
|
|
RefPtr<nsFrameSelection> frameSelection = aPresShell->FrameSelection();
|
|
|
|
nsCOMPtr<nsISelection> domSelection;
|
|
if (frameSelection) {
|
|
domSelection = frameSelection->
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> startNode, endNode;
|
|
bool isCollapsed = false;
|
|
nsCOMPtr<nsIContent> startContent, endContent;
|
|
int32_t startOffset = 0;
|
|
if (domSelection) {
|
|
domSelection->GetIsCollapsed(&isCollapsed);
|
|
nsCOMPtr<nsIDOMRange> domRange;
|
|
rv = domSelection->GetRangeAt(0, getter_AddRefs(domRange));
|
|
if (domRange) {
|
|
domRange->GetStartContainer(getter_AddRefs(startNode));
|
|
domRange->GetEndContainer(getter_AddRefs(endNode));
|
|
domRange->GetStartOffset(&startOffset);
|
|
|
|
nsIContent *childContent = nullptr;
|
|
|
|
startContent = do_QueryInterface(startNode);
|
|
if (startContent && startContent->IsElement()) {
|
|
NS_ASSERTION(startOffset >= 0, "Start offset cannot be negative");
|
|
childContent = startContent->GetChildAt(startOffset);
|
|
if (childContent) {
|
|
startContent = childContent;
|
|
}
|
|
}
|
|
|
|
endContent = do_QueryInterface(endNode);
|
|
if (endContent && endContent->IsElement()) {
|
|
int32_t endOffset = 0;
|
|
domRange->GetEndOffset(&endOffset);
|
|
NS_ASSERTION(endOffset >= 0, "End offset cannot be negative");
|
|
childContent = endContent->GetChildAt(endOffset);
|
|
if (childContent) {
|
|
endContent = childContent;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
rv = NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
nsIFrame *startFrame = nullptr;
|
|
if (startContent) {
|
|
startFrame = startContent->GetPrimaryFrame();
|
|
if (isCollapsed) {
|
|
// Next check to see if our caret is at the very end of a node
|
|
// If so, the caret is actually sitting in front of the next
|
|
// logical frame's primary node - so for this case we need to
|
|
// change caretContent to that node.
|
|
|
|
if (startContent->NodeType() == nsIDOMNode::TEXT_NODE) {
|
|
nsAutoString nodeValue;
|
|
startContent->AppendTextTo(nodeValue);
|
|
|
|
bool isFormControl =
|
|
startContent->IsNodeOfType(nsINode::eHTML_FORM_CONTROL);
|
|
|
|
if (nodeValue.Length() == (uint32_t)startOffset && !isFormControl &&
|
|
startContent != aDocument->GetRootElement()) {
|
|
// Yes, indeed we were at the end of the last node
|
|
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
|
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
|
presContext, startFrame,
|
|
eLeaf,
|
|
false, // aVisual
|
|
false, // aLockInScrollView
|
|
true, // aFollowOOFs
|
|
false // aSkipPopupChecks
|
|
);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
nsIFrame *newCaretFrame = nullptr;
|
|
nsCOMPtr<nsIContent> newCaretContent = startContent;
|
|
bool endOfSelectionInStartNode(startContent == endContent);
|
|
do {
|
|
// Continue getting the next frame until the primary content for the frame
|
|
// we are on changes - we don't want to be stuck in the same place
|
|
frameTraversal->Next();
|
|
newCaretFrame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
if (nullptr == newCaretFrame)
|
|
break;
|
|
newCaretContent = newCaretFrame->GetContent();
|
|
} while (!newCaretContent || newCaretContent == startContent);
|
|
|
|
if (newCaretFrame && newCaretContent) {
|
|
// If the caret is exactly at the same position of the new frame,
|
|
// then we can use the newCaretFrame and newCaretContent for our position
|
|
nsRect caretRect;
|
|
nsIFrame *frame = nsCaret::GetGeometry(domSelection, &caretRect);
|
|
if (frame) {
|
|
nsPoint caretWidgetOffset;
|
|
nsIWidget *widget = frame->GetNearestWidget(caretWidgetOffset);
|
|
caretRect.MoveBy(caretWidgetOffset);
|
|
nsPoint newCaretOffset;
|
|
nsIWidget *newCaretWidget = newCaretFrame->GetNearestWidget(newCaretOffset);
|
|
if (widget == newCaretWidget && caretRect.y == newCaretOffset.y &&
|
|
caretRect.x == newCaretOffset.x) {
|
|
// The caret is at the start of the new element.
|
|
startFrame = newCaretFrame;
|
|
startContent = newCaretContent;
|
|
if (endOfSelectionInStartNode) {
|
|
endContent = newCaretContent; // Ensure end of selection is not before start
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*aStartContent = startContent;
|
|
*aEndContent = endContent;
|
|
NS_IF_ADDREF(*aStartContent);
|
|
NS_IF_ADDREF(*aEndContent);
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
|
|
nsIContent* aStartContent,
|
|
int32_t aType, bool aNoParentTraversal,
|
|
nsIContent** aNextContent)
|
|
{
|
|
*aNextContent = nullptr;
|
|
|
|
// True if we are navigating by document (F6/Shift+F6) or false if we are
|
|
// navigating by element (Tab/Shift+Tab).
|
|
bool forDocumentNavigation = false;
|
|
|
|
// This is used for document navigation only. It will be set to true if we
|
|
// start navigating from a starting point. If this starting point is near the
|
|
// end of the document (for example, an element on a statusbar), and there
|
|
// are no child documents or panels before the end of the document, then we
|
|
// will need to ensure that we don't consider the root chrome window when we
|
|
// loop around and instead find the next child document/panel, as focus is
|
|
// already in that window. This flag will be cleared once we navigate into
|
|
// another document.
|
|
bool mayFocusRoot = (aStartContent != nullptr);
|
|
|
|
nsCOMPtr<nsIContent> startContent = aStartContent;
|
|
if (!startContent && aType != MOVEFOCUS_CARET) {
|
|
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
|
|
// When moving between documents, make sure to get the right
|
|
// starting content in a descendant.
|
|
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
|
startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
|
|
}
|
|
else if (aType != MOVEFOCUS_LASTDOC) {
|
|
// Otherwise, start at the focused node. If MOVEFOCUS_LASTDOC is used,
|
|
// then we are document-navigating backwards from chrome to the content
|
|
// process, and we don't want to use this so that we start from the end
|
|
// of the document.
|
|
startContent = aWindow->GetFocusedNode();
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIDocument> doc;
|
|
if (startContent)
|
|
doc = startContent->GetComposedDoc();
|
|
else
|
|
doc = aWindow->GetExtantDoc();
|
|
if (!doc)
|
|
return NS_OK;
|
|
|
|
LookAndFeel::GetInt(LookAndFeel::eIntID_TabFocusModel,
|
|
&nsIContent::sTabFocusModel);
|
|
|
|
// These types are for document navigation using F6.
|
|
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC ||
|
|
aType == MOVEFOCUS_FIRSTDOC || aType == MOVEFOCUS_LASTDOC) {
|
|
forDocumentNavigation = true;
|
|
}
|
|
|
|
// If moving to the root or first document, find the root element and return.
|
|
if (aType == MOVEFOCUS_ROOT || aType == MOVEFOCUS_FIRSTDOC) {
|
|
NS_IF_ADDREF(*aNextContent = GetRootForFocus(aWindow, doc, false, false));
|
|
if (!*aNextContent && aType == MOVEFOCUS_FIRSTDOC) {
|
|
// When looking for the first document, if the root wasn't focusable,
|
|
// find the next focusable document.
|
|
aType = MOVEFOCUS_FORWARDDOC;
|
|
} else {
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
nsIContent* rootContent = doc->GetRootElement();
|
|
NS_ENSURE_TRUE(rootContent, NS_OK);
|
|
|
|
nsIPresShell *presShell = doc->GetShell();
|
|
NS_ENSURE_TRUE(presShell, NS_OK);
|
|
|
|
if (aType == MOVEFOCUS_FIRST) {
|
|
if (!aStartContent)
|
|
startContent = rootContent;
|
|
return GetNextTabbableContent(presShell, startContent,
|
|
nullptr, startContent,
|
|
true, 1, false, false, aNextContent);
|
|
}
|
|
if (aType == MOVEFOCUS_LAST) {
|
|
if (!aStartContent)
|
|
startContent = rootContent;
|
|
return GetNextTabbableContent(presShell, startContent,
|
|
nullptr, startContent,
|
|
false, 0, false, false, aNextContent);
|
|
}
|
|
|
|
bool forward = (aType == MOVEFOCUS_FORWARD ||
|
|
aType == MOVEFOCUS_FORWARDDOC ||
|
|
aType == MOVEFOCUS_CARET);
|
|
bool doNavigation = true;
|
|
bool ignoreTabIndex = false;
|
|
// when a popup is open, we want to ensure that tab navigation occurs only
|
|
// within the most recently opened panel. If a popup is open, its frame will
|
|
// be stored in popupFrame.
|
|
nsIFrame* popupFrame = nullptr;
|
|
|
|
int32_t tabIndex = forward ? 1 : 0;
|
|
if (startContent) {
|
|
nsIFrame* frame = startContent->GetPrimaryFrame();
|
|
if (startContent->IsHTMLElement(nsGkAtoms::area))
|
|
startContent->IsFocusable(&tabIndex);
|
|
else if (frame)
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
else
|
|
startContent->IsFocusable(&tabIndex);
|
|
|
|
// if the current element isn't tabbable, ignore the tabindex and just
|
|
// look for the next element. The root content won't have a tabindex
|
|
// so just treat this as the beginning of the tab order.
|
|
if (tabIndex < 0) {
|
|
tabIndex = 1;
|
|
if (startContent != rootContent)
|
|
ignoreTabIndex = true;
|
|
}
|
|
|
|
// check if the focus is currently inside a popup. Elements such as the
|
|
// autocomplete widget use the noautofocus attribute to allow the focus to
|
|
// remain outside the popup when it is opened.
|
|
if (frame) {
|
|
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
|
nsGkAtoms::menuPopupFrame);
|
|
}
|
|
|
|
if (popupFrame && !forDocumentNavigation) {
|
|
// Don't navigate outside of a popup, so pretend that the
|
|
// root content is the popup itself
|
|
rootContent = popupFrame->GetContent();
|
|
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
|
}
|
|
else if (!forward) {
|
|
// If focus moves backward and when current focused node is root
|
|
// content or <body> element which is editable by contenteditable
|
|
// attribute, focus should move to its parent document.
|
|
if (startContent == rootContent) {
|
|
doNavigation = false;
|
|
} else {
|
|
nsIDocument* doc = startContent->GetComposedDoc();
|
|
if (startContent ==
|
|
nsLayoutUtils::GetEditableRootContentByContentEditable(doc)) {
|
|
doNavigation = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#ifdef MOZ_XUL
|
|
if (aType != MOVEFOCUS_CARET) {
|
|
// if there is no focus, yet a panel is open, focus the first item in
|
|
// the panel
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
if (pm)
|
|
popupFrame = pm->GetTopPopup(ePopupTypePanel);
|
|
}
|
|
#endif
|
|
if (popupFrame) {
|
|
// When there is a popup open, and no starting content, start the search
|
|
// at the topmost popup.
|
|
startContent = popupFrame->GetContent();
|
|
NS_ASSERTION(startContent, "Popup frame doesn't have a content node");
|
|
// Unless we are searching for documents, set the root content to the
|
|
// popup as well, so that we don't tab-navigate outside the popup.
|
|
// When navigating by documents, we start at the popup but can navigate
|
|
// outside of it to look for other panels and documents.
|
|
if (!forDocumentNavigation) {
|
|
rootContent = startContent;
|
|
}
|
|
|
|
doc = startContent ? startContent->GetComposedDoc() : nullptr;
|
|
}
|
|
else {
|
|
// Otherwise, for content shells, start from the location of the caret.
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
if (docShell && docShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
|
|
nsCOMPtr<nsIContent> endSelectionContent;
|
|
GetSelectionLocation(doc, presShell,
|
|
getter_AddRefs(startContent),
|
|
getter_AddRefs(endSelectionContent));
|
|
// If the selection is on the rootContent, then there is no selection
|
|
if (startContent == rootContent) {
|
|
startContent = nullptr;
|
|
}
|
|
|
|
if (aType == MOVEFOCUS_CARET) {
|
|
// GetFocusInSelection finds a focusable link near the caret.
|
|
// If there is no start content though, don't do this to avoid
|
|
// focusing something unexpected.
|
|
if (startContent) {
|
|
GetFocusInSelection(aWindow, startContent,
|
|
endSelectionContent, aNextContent);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
if (startContent) {
|
|
// when starting from a selection, we always want to find the next or
|
|
// previous element in the document. So the tabindex on elements
|
|
// should be ignored.
|
|
ignoreTabIndex = true;
|
|
}
|
|
}
|
|
|
|
if (!startContent) {
|
|
// otherwise, just use the root content as the starting point
|
|
startContent = rootContent;
|
|
NS_ENSURE_TRUE(startContent, NS_OK);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_ASSERTION(startContent, "starting content not set");
|
|
|
|
// keep a reference to the starting content. If we find that again, it means
|
|
// we've iterated around completely and we don't want to adjust the focus.
|
|
// The skipOriginalContentCheck will be set to true only for the first time
|
|
// GetNextTabbableContent is called. This ensures that we don't break out
|
|
// when nothing is focused to start with. Specifically,
|
|
// GetNextTabbableContent first checks the root content -- which happens to
|
|
// be the same as the start content -- when nothing is focused and tabbing
|
|
// forward. Without skipOriginalContentCheck set to true, we'd end up
|
|
// returning right away and focusing nothing. Luckily, GetNextTabbableContent
|
|
// will never wrap around on its own, and can only return the original
|
|
// content when it is called a second time or later.
|
|
bool skipOriginalContentCheck = true;
|
|
nsIContent* originalStartContent = startContent;
|
|
|
|
LOGCONTENTNAVIGATION("Focus Navigation Start Content %s", startContent.get());
|
|
LOGFOCUSNAVIGATION((" Forward: %d Tabindex: %d Ignore: %d DocNav: %d",
|
|
forward, tabIndex, ignoreTabIndex, forDocumentNavigation));
|
|
|
|
while (doc) {
|
|
if (doNavigation) {
|
|
nsCOMPtr<nsIContent> nextFocus;
|
|
nsresult rv = GetNextTabbableContent(presShell, rootContent,
|
|
skipOriginalContentCheck ? nullptr : originalStartContent,
|
|
startContent, forward,
|
|
tabIndex, ignoreTabIndex,
|
|
forDocumentNavigation,
|
|
getter_AddRefs(nextFocus));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
|
|
// Navigation was redirected to a child process, so just return.
|
|
return NS_OK;
|
|
}
|
|
|
|
// found a content node to focus.
|
|
if (nextFocus) {
|
|
LOGCONTENTNAVIGATION("Next Content: %s", nextFocus.get());
|
|
|
|
// as long as the found node was not the same as the starting node,
|
|
// set it as the return value. For document navigation, we can return
|
|
// the same element in case there is only one content node that could
|
|
// be returned, for example, in a child process document.
|
|
if (nextFocus != originalStartContent || forDocumentNavigation) {
|
|
nextFocus.forget(aNextContent);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
if (popupFrame && !forDocumentNavigation) {
|
|
// in a popup, so start again from the beginning of the popup. However,
|
|
// if we already started at the beginning, then there isn't anything to
|
|
// focus, so just return
|
|
if (startContent != rootContent) {
|
|
startContent = rootContent;
|
|
tabIndex = forward ? 1 : 0;
|
|
continue;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
doNavigation = true;
|
|
skipOriginalContentCheck = forDocumentNavigation;
|
|
ignoreTabIndex = false;
|
|
|
|
if (aNoParentTraversal) {
|
|
if (startContent == rootContent)
|
|
return NS_OK;
|
|
|
|
startContent = rootContent;
|
|
tabIndex = forward ? 1 : 0;
|
|
continue;
|
|
}
|
|
|
|
// Reached the beginning or end of the document. Next, navigate up to the
|
|
// parent document and try again.
|
|
nsCOMPtr<nsPIDOMWindow> piWindow = doc->GetWindow();
|
|
NS_ENSURE_TRUE(piWindow, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIDocShell> docShell = piWindow->GetDocShell();
|
|
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
|
|
|
|
// Get the frame element this window is inside and, from that, get the
|
|
// parent document and presshell. If there is no enclosing frame element,
|
|
// then this is a top-level, embedded or remote window.
|
|
startContent = piWindow->GetFrameElementInternal();
|
|
if (startContent) {
|
|
doc = startContent->GetComposedDoc();
|
|
NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
|
|
|
|
rootContent = doc->GetRootElement();
|
|
presShell = doc->GetShell();
|
|
|
|
// We can focus the root element now that we have moved to another document.
|
|
mayFocusRoot = true;
|
|
|
|
nsIFrame* frame = startContent->GetPrimaryFrame();
|
|
if (!frame) {
|
|
return NS_OK;
|
|
}
|
|
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
if (tabIndex < 0) {
|
|
tabIndex = 1;
|
|
ignoreTabIndex = true;
|
|
}
|
|
|
|
// if the frame is inside a popup, make sure to scan only within the
|
|
// popup. This handles the situation of tabbing amongst elements
|
|
// inside an iframe which is itself inside a popup. Otherwise,
|
|
// navigation would move outside the popup when tabbing outside the
|
|
// iframe.
|
|
if (!forDocumentNavigation) {
|
|
popupFrame = nsLayoutUtils::GetClosestFrameOfType(frame,
|
|
nsGkAtoms::menuPopupFrame);
|
|
if (popupFrame) {
|
|
rootContent = popupFrame->GetContent();
|
|
NS_ASSERTION(rootContent, "Popup frame doesn't have a content node");
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// There is no parent, so call the tree owner. This will tell the
|
|
// embedder or parent process that it should take the focus.
|
|
bool tookFocus;
|
|
docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
|
|
// If the tree owner took the focus, blur the current content.
|
|
if (tookFocus) {
|
|
nsCOMPtr<nsPIDOMWindow> window = docShell->GetWindow();
|
|
if (window->GetFocusedNode() == mFocusedContent)
|
|
Blur(mFocusedWindow, nullptr, true, true);
|
|
else
|
|
window->SetFocusedNode(nullptr);
|
|
return NS_OK;
|
|
}
|
|
|
|
// If we have reached the end of the top-level document, focus the
|
|
// first element in the top-level document. This should always happen
|
|
// when navigating by document forwards but when navigating backwards,
|
|
// only do this if we started in another document or within a popup frame.
|
|
// If the focus started in this window outside a popup however, we should
|
|
// continue by looping around to the end again.
|
|
if (forDocumentNavigation && (forward || mayFocusRoot || popupFrame)) {
|
|
// HTML content documents can have their root element focused (a focus
|
|
// ring appears around the entire content area frame). This root
|
|
// appears in the tab order before all of the elements in the document.
|
|
// Chrome documents however cannot be focused directly, so instead we
|
|
// focus the first focusable element within the window.
|
|
// For example, the urlbar.
|
|
nsIContent* root = GetRootForFocus(piWindow, doc, true, true);
|
|
return FocusFirst(root, aNextContent);
|
|
}
|
|
|
|
// reset the tab index and start again from the beginning or end
|
|
startContent = rootContent;
|
|
tabIndex = forward ? 1 : 0;
|
|
}
|
|
|
|
// wrapped all the way around and didn't find anything to move the focus
|
|
// to, so just break out
|
|
if (startContent == originalStartContent)
|
|
break;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::GetNextTabbableContent(nsIPresShell* aPresShell,
|
|
nsIContent* aRootContent,
|
|
nsIContent* aOriginalStartContent,
|
|
nsIContent* aStartContent,
|
|
bool aForward,
|
|
int32_t aCurrentTabIndex,
|
|
bool aIgnoreTabIndex,
|
|
bool aForDocumentNavigation,
|
|
nsIContent** aResultContent)
|
|
{
|
|
*aResultContent = nullptr;
|
|
|
|
nsCOMPtr<nsIContent> startContent = aStartContent;
|
|
if (!startContent)
|
|
return NS_OK;
|
|
|
|
LOGCONTENTNAVIGATION("GetNextTabbable: %s", aStartContent);
|
|
LOGFOCUSNAVIGATION((" tabindex: %d", aCurrentTabIndex));
|
|
|
|
nsPresContext* presContext = aPresShell->GetPresContext();
|
|
|
|
bool getNextFrame = true;
|
|
nsCOMPtr<nsIContent> iterStartContent = aStartContent;
|
|
while (1) {
|
|
nsIFrame* startFrame = iterStartContent->GetPrimaryFrame();
|
|
// if there is no frame, look for another content node that has a frame
|
|
if (!startFrame) {
|
|
// if the root content doesn't have a frame, just return
|
|
if (iterStartContent == aRootContent)
|
|
return NS_OK;
|
|
|
|
// look for the next or previous content node in tree order
|
|
iterStartContent = aForward ? iterStartContent->GetNextNode() : iterStartContent->GetPreviousContent();
|
|
// we've already skipped over the initial focused content, so we
|
|
// don't want to traverse frames.
|
|
getNextFrame = false;
|
|
if (iterStartContent)
|
|
continue;
|
|
|
|
// otherwise, as a last attempt, just look at the root content
|
|
iterStartContent = aRootContent;
|
|
continue;
|
|
}
|
|
|
|
// For tab navigation, pass false for aSkipPopupChecks so that we don't
|
|
// iterate into or out of a popup. For document naviation pass true to
|
|
// ignore these boundaries.
|
|
nsCOMPtr<nsIFrameEnumerator> frameTraversal;
|
|
nsresult rv = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
|
|
presContext, startFrame,
|
|
ePreOrder,
|
|
false, // aVisual
|
|
false, // aLockInScrollView
|
|
true, // aFollowOOFs
|
|
aForDocumentNavigation // aSkipPopupChecks
|
|
);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (iterStartContent == aRootContent) {
|
|
if (!aForward) {
|
|
frameTraversal->Last();
|
|
} else if (aRootContent->IsFocusable()) {
|
|
frameTraversal->Next();
|
|
}
|
|
}
|
|
else if (getNextFrame &&
|
|
(!iterStartContent ||
|
|
!iterStartContent->IsHTMLElement(nsGkAtoms::area))) {
|
|
// Need to do special check in case we're in an imagemap which has multiple
|
|
// content nodes per frame, so don't skip over the starting frame.
|
|
if (aForward)
|
|
frameTraversal->Next();
|
|
else
|
|
frameTraversal->Prev();
|
|
}
|
|
|
|
// Walk frames to find something tabbable matching mCurrentTabIndex
|
|
nsIFrame* frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
while (frame) {
|
|
nsIContent* currentContent = frame->GetContent();
|
|
|
|
// For document navigation, check if this element is an open panel. Since
|
|
// panels aren't focusable (tabIndex would be -1), we'll just assume that
|
|
// for document navigation, the tabIndex is 0.
|
|
if (aForDocumentNavigation && currentContent && (aCurrentTabIndex == 0) &&
|
|
currentContent->IsXULElement(nsGkAtoms::panel)) {
|
|
nsMenuPopupFrame* popupFrame = do_QueryFrame(frame);
|
|
// Check if the panel is open. Closed panels are ignored since you can't
|
|
// focus anything in them.
|
|
if (popupFrame && popupFrame->IsOpen()) {
|
|
// When moving backward, skip the popup we started in otherwise it
|
|
// will be selected again.
|
|
bool validPopup = true;
|
|
if (!aForward) {
|
|
nsIContent* content = aStartContent;
|
|
while (content) {
|
|
if (content == currentContent) {
|
|
validPopup = false;
|
|
break;
|
|
}
|
|
|
|
content = content->GetParent();
|
|
}
|
|
}
|
|
|
|
if (validPopup) {
|
|
// Since a panel isn't focusable itself, find the first focusable
|
|
// content within the popup. If there isn't any focusable content
|
|
// in the popup, skip this popup and continue iterating through the
|
|
// frames. We pass the panel itself (currentContent) as the starting
|
|
// and root content, so that we only find content within the panel.
|
|
// Note also that we pass false for aForDocumentNavigation since we
|
|
// want to locate the first content, not the first document.
|
|
rv = GetNextTabbableContent(aPresShell, currentContent,
|
|
nullptr, currentContent,
|
|
true, 1, false, false,
|
|
aResultContent);
|
|
if (NS_SUCCEEDED(rv) && *aResultContent) {
|
|
return rv;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// TabIndex not set defaults to 0 for form elements, anchors and other
|
|
// elements that are normally focusable. Tabindex defaults to -1
|
|
// for elements that are not normally focusable.
|
|
// The returned computed tabindex from IsFocusable() is as follows:
|
|
// < 0 not tabbable at all
|
|
// == 0 in normal tab order (last after positive tabindexed items)
|
|
// > 0 can be tabbed to in the order specified by this value
|
|
int32_t tabIndex;
|
|
frame->IsFocusable(&tabIndex, 0);
|
|
|
|
LOGCONTENTNAVIGATION("Next Tabbable %s:", frame->GetContent());
|
|
LOGFOCUSNAVIGATION((" with tabindex: %d expected: %d", tabIndex, aCurrentTabIndex));
|
|
|
|
if (tabIndex >= 0) {
|
|
NS_ASSERTION(currentContent, "IsFocusable set a tabindex for a frame with no content");
|
|
if (!aForDocumentNavigation &&
|
|
currentContent->IsHTMLElement(nsGkAtoms::img) &&
|
|
currentContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usemap)) {
|
|
// This is an image with a map. Image map areas are not traversed by
|
|
// nsIFrameTraversal so look for the next or previous area element.
|
|
nsIContent *areaContent =
|
|
GetNextTabbableMapArea(aForward, aCurrentTabIndex,
|
|
currentContent, iterStartContent);
|
|
if (areaContent) {
|
|
NS_ADDREF(*aResultContent = areaContent);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else if (aIgnoreTabIndex || aCurrentTabIndex == tabIndex) {
|
|
// break out if we've wrapped around to the start again.
|
|
if (aOriginalStartContent && currentContent == aOriginalStartContent) {
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
return NS_OK;
|
|
}
|
|
|
|
// If this is a remote child browser, call NavigateDocument to have
|
|
// the child process continue the navigation. Return a special error
|
|
// code to have the caller return early. If the child ends up not
|
|
// being focusable in some way, the child process will call back
|
|
// into document navigation again by calling MoveFocus.
|
|
TabParent* remote = TabParent::GetFrom(currentContent);
|
|
if (remote) {
|
|
remote->NavigateByKey(aForward, aForDocumentNavigation);
|
|
return NS_SUCCESS_DOM_NO_OPERATION;
|
|
}
|
|
|
|
// Next, for document navigation, check if this a non-remote child document.
|
|
bool checkSubDocument = true;
|
|
if (aForDocumentNavigation) {
|
|
nsIContent* docRoot = GetRootForChildDocument(currentContent);
|
|
if (docRoot) {
|
|
// If GetRootForChildDocument returned something then call
|
|
// FocusFirst to find the root or first element to focus within
|
|
// the child document. If this is a frameset though, skip this and
|
|
// fall through to the checkSubDocument block below to iterate into
|
|
// the frameset's frames and locate the first focusable frame.
|
|
if (!docRoot->IsHTMLElement(nsGkAtoms::frameset)) {
|
|
return FocusFirst(docRoot, aResultContent);
|
|
}
|
|
} else {
|
|
// Set checkSubDocument to false, as this was neither a frame
|
|
// type element or a child document that was focusable.
|
|
checkSubDocument = false;
|
|
}
|
|
}
|
|
|
|
if (checkSubDocument) {
|
|
// found a node with a matching tab index. Check if it is a child
|
|
// frame. If so, navigate into the child frame instead.
|
|
nsIDocument* doc = currentContent->GetComposedDoc();
|
|
NS_ASSERTION(doc, "content not in document");
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(currentContent);
|
|
if (subdoc && !subdoc->EventHandlingSuppressed()) {
|
|
if (aForward) {
|
|
// when tabbing forward into a frame, return the root
|
|
// frame so that the canvas becomes focused.
|
|
nsCOMPtr<nsPIDOMWindow> subframe = subdoc->GetWindow();
|
|
if (subframe) {
|
|
*aResultContent = GetRootForFocus(subframe, subdoc, false, true);
|
|
if (*aResultContent) {
|
|
NS_ADDREF(*aResultContent);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
Element* rootElement = subdoc->GetRootElement();
|
|
nsIPresShell* subShell = subdoc->GetShell();
|
|
if (rootElement && subShell) {
|
|
rv = GetNextTabbableContent(subShell, rootElement,
|
|
aOriginalStartContent, rootElement,
|
|
aForward, (aForward ? 1 : 0),
|
|
false, aForDocumentNavigation, aResultContent);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
if (*aResultContent)
|
|
return NS_OK;
|
|
}
|
|
}
|
|
// otherwise, use this as the next content node to tab to, unless
|
|
// this was the element we started on. This would happen for
|
|
// instance on an element with child frames, where frame navigation
|
|
// could return the original element again. In that case, just skip
|
|
// it. Also, if the next content node is the root content, then
|
|
// return it. This latter case would happen only if someone made a
|
|
// popup focusable.
|
|
// Also, when going backwards, check to ensure that the focus
|
|
// wouldn't be redirected. Otherwise, for example, when an input in
|
|
// a textbox is focused, the enclosing textbox would be found and
|
|
// the same inner input would be returned again.
|
|
else if (currentContent == aRootContent ||
|
|
(currentContent != startContent &&
|
|
(aForward || !GetRedirectedFocus(currentContent)))) {
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
return NS_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (aOriginalStartContent && currentContent == aOriginalStartContent) {
|
|
// not focusable, so return if we have wrapped around to the original
|
|
// content. This is necessary in case the original starting content was
|
|
// not focusable.
|
|
NS_ADDREF(*aResultContent = currentContent);
|
|
return NS_OK;
|
|
}
|
|
|
|
// Move to the next or previous frame, but ignore continuation frames
|
|
// since only the first frame should be involved in focusability.
|
|
// Otherwise, a loop will occur in the following example:
|
|
// <span tabindex="1">...<a/><a/>...</span>
|
|
// where the text wraps onto multiple lines. Tabbing from the second
|
|
// link can find one of the span's continuation frames between the link
|
|
// and the end of the span, and the span would end up getting focused
|
|
// again.
|
|
do {
|
|
if (aForward)
|
|
frameTraversal->Next();
|
|
else
|
|
frameTraversal->Prev();
|
|
frame = static_cast<nsIFrame*>(frameTraversal->CurrentItem());
|
|
} while (frame && frame->GetPrevContinuation());
|
|
}
|
|
|
|
// If already at lowest priority tab (0), end search completely.
|
|
// A bit counterintuitive but true, tabindex order goes 1, 2, ... 32767, 0
|
|
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
|
|
// if going backwards, the canvas should be focused once the beginning
|
|
// has been reached, so get the root element.
|
|
if (!aForward) {
|
|
nsCOMPtr<nsPIDOMWindow> window = GetCurrentWindow(aRootContent);
|
|
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
|
|
|
nsCOMPtr<nsIContent> docRoot =
|
|
GetRootForFocus(window, aRootContent->GetComposedDoc(), false, true);
|
|
FocusFirst(docRoot, aResultContent);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// continue looking for next highest priority tabindex
|
|
aCurrentTabIndex = GetNextTabIndex(aRootContent, aCurrentTabIndex, aForward);
|
|
startContent = iterStartContent = aRootContent;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIContent*
|
|
nsFocusManager::GetNextTabbableMapArea(bool aForward,
|
|
int32_t aCurrentTabIndex,
|
|
nsIContent* aImageContent,
|
|
nsIContent* aStartContent)
|
|
{
|
|
nsAutoString useMap;
|
|
aImageContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, useMap);
|
|
|
|
nsCOMPtr<nsIDocument> doc = aImageContent->GetComposedDoc();
|
|
if (doc) {
|
|
nsCOMPtr<nsIContent> mapContent = doc->FindImageMap(useMap);
|
|
if (!mapContent)
|
|
return nullptr;
|
|
uint32_t count = mapContent->GetChildCount();
|
|
// First see if the the start content is in this map
|
|
|
|
int32_t index = mapContent->IndexOf(aStartContent);
|
|
int32_t tabIndex;
|
|
if (index < 0 || (aStartContent->IsFocusable(&tabIndex) &&
|
|
tabIndex != aCurrentTabIndex)) {
|
|
// If aStartContent is in this map we must start iterating past it.
|
|
// We skip the case where aStartContent has tabindex == aStartContent
|
|
// since the next tab ordered element might be before it
|
|
// (or after for backwards) in the child list.
|
|
index = aForward ? -1 : (int32_t)count;
|
|
}
|
|
|
|
// GetChildAt will return nullptr if our index < 0 or index >= count
|
|
nsCOMPtr<nsIContent> areaContent;
|
|
while ((areaContent = mapContent->GetChildAt(aForward ? ++index : --index)) != nullptr) {
|
|
if (areaContent->IsFocusable(&tabIndex) && tabIndex == aCurrentTabIndex) {
|
|
return areaContent;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
int32_t
|
|
nsFocusManager::GetNextTabIndex(nsIContent* aParent,
|
|
int32_t aCurrentTabIndex,
|
|
bool aForward)
|
|
{
|
|
int32_t tabIndex, childTabIndex;
|
|
|
|
if (aForward) {
|
|
tabIndex = 0;
|
|
for (nsIContent* child = aParent->GetFirstChild();
|
|
child;
|
|
child = child->GetNextSibling()) {
|
|
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
|
if (childTabIndex > aCurrentTabIndex && childTabIndex != tabIndex) {
|
|
tabIndex = (tabIndex == 0 || childTabIndex < tabIndex) ? childTabIndex : tabIndex;
|
|
}
|
|
|
|
nsAutoString tabIndexStr;
|
|
child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
|
|
nsresult ec;
|
|
int32_t val = tabIndexStr.ToInteger(&ec);
|
|
if (NS_SUCCEEDED (ec) && val > aCurrentTabIndex && val != tabIndex) {
|
|
tabIndex = (tabIndex == 0 || val < tabIndex) ? val : tabIndex;
|
|
}
|
|
}
|
|
}
|
|
else { /* !aForward */
|
|
tabIndex = 1;
|
|
for (nsIContent* child = aParent->GetFirstChild();
|
|
child;
|
|
child = child->GetNextSibling()) {
|
|
childTabIndex = GetNextTabIndex(child, aCurrentTabIndex, aForward);
|
|
if ((aCurrentTabIndex == 0 && childTabIndex > tabIndex) ||
|
|
(childTabIndex < aCurrentTabIndex && childTabIndex > tabIndex)) {
|
|
tabIndex = childTabIndex;
|
|
}
|
|
|
|
nsAutoString tabIndexStr;
|
|
child->GetAttr(kNameSpaceID_None, nsGkAtoms::tabindex, tabIndexStr);
|
|
nsresult ec;
|
|
int32_t val = tabIndexStr.ToInteger(&ec);
|
|
if (NS_SUCCEEDED (ec)) {
|
|
if ((aCurrentTabIndex == 0 && val > tabIndex) ||
|
|
(val < aCurrentTabIndex && val > tabIndex) ) {
|
|
tabIndex = val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return tabIndex;
|
|
}
|
|
|
|
nsresult
|
|
nsFocusManager::FocusFirst(nsIContent* aRootContent, nsIContent** aNextContent)
|
|
{
|
|
if (!aRootContent) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIDocument* doc = aRootContent->GetComposedDoc();
|
|
if (doc) {
|
|
nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell();
|
|
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
|
// If the found content is in a chrome shell, navigate forward one
|
|
// tabbable item so that the first item is focused. Note that we
|
|
// always go forward and not back here.
|
|
nsIPresShell* presShell = doc->GetShell();
|
|
if (presShell) {
|
|
return GetNextTabbableContent(presShell, aRootContent,
|
|
nullptr, aRootContent,
|
|
true, 1, false, false,
|
|
aNextContent);
|
|
}
|
|
}
|
|
}
|
|
|
|
NS_ADDREF(*aNextContent = aRootContent);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIContent*
|
|
nsFocusManager::GetRootForFocus(nsPIDOMWindow* aWindow,
|
|
nsIDocument* aDocument,
|
|
bool aForDocumentNavigation,
|
|
bool aCheckVisibility)
|
|
{
|
|
if (!aForDocumentNavigation) {
|
|
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
|
|
if (docShell->ItemType() == nsIDocShellTreeItem::typeChrome) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (aCheckVisibility && !IsWindowVisible(aWindow))
|
|
return nullptr;
|
|
|
|
// If the body is contenteditable, use the editor's root element rather than
|
|
// the actual root element.
|
|
nsCOMPtr<nsIContent> rootElement =
|
|
nsLayoutUtils::GetEditableRootContentByContentEditable(aDocument);
|
|
if (!rootElement || !rootElement->GetPrimaryFrame()) {
|
|
rootElement = aDocument->GetRootElement();
|
|
if (!rootElement) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (aCheckVisibility && !rootElement->GetPrimaryFrame()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Finally, check if this is a frameset
|
|
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDocument);
|
|
if (htmlDoc) {
|
|
nsIContent* htmlChild = aDocument->GetHtmlChildElement(nsGkAtoms::frameset);
|
|
if (htmlChild) {
|
|
// In document navigation mode, return the frameset so that navigation
|
|
// descends into the child frames.
|
|
return aForDocumentNavigation ? htmlChild : nullptr;
|
|
}
|
|
}
|
|
|
|
return rootElement;
|
|
}
|
|
|
|
nsIContent*
|
|
nsFocusManager::GetRootForChildDocument(nsIContent* aContent)
|
|
{
|
|
// Check for elements that represent child documents, that is, browsers,
|
|
// editors or frames from a frameset. We don't include iframes since we
|
|
// consider them to be an integral part of the same window or page.
|
|
if (!aContent ||
|
|
!(aContent->IsXULElement(nsGkAtoms::browser) ||
|
|
aContent->IsXULElement(nsGkAtoms::editor) ||
|
|
aContent->IsHTMLElement(nsGkAtoms::frame))) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsIDocument* doc = aContent->GetComposedDoc();
|
|
if (!doc) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsIDocument* subdoc = doc->GetSubDocumentFor(aContent);
|
|
if (!subdoc || subdoc->EventHandlingSuppressed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindow> window = subdoc->GetWindow();
|
|
return GetRootForFocus(window, subdoc, true, true);
|
|
}
|
|
|
|
void
|
|
nsFocusManager::GetFocusInSelection(nsPIDOMWindow* aWindow,
|
|
nsIContent* aStartSelection,
|
|
nsIContent* aEndSelection,
|
|
nsIContent** aFocusedContent)
|
|
{
|
|
*aFocusedContent = nullptr;
|
|
|
|
nsCOMPtr<nsIContent> testContent = aStartSelection;
|
|
nsCOMPtr<nsIContent> nextTestContent = aEndSelection;
|
|
|
|
nsCOMPtr<nsIContent> currentFocus = aWindow->GetFocusedNode();
|
|
|
|
// We now have the correct start node in selectionContent!
|
|
// Search for focusable elements, starting with selectionContent
|
|
|
|
// Method #1: Keep going up while we look - an ancestor might be focusable
|
|
// We could end the loop earlier, such as when we're no longer
|
|
// in the same frame, by comparing selectionContent->GetPrimaryFrame()
|
|
// with a variable holding the starting selectionContent
|
|
while (testContent) {
|
|
// Keep testing while selectionContent is equal to something,
|
|
// eventually we'll run out of ancestors
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (testContent == currentFocus ||
|
|
testContent->IsLink(getter_AddRefs(uri))) {
|
|
testContent.forget(aFocusedContent);
|
|
return;
|
|
}
|
|
|
|
// Get the parent
|
|
testContent = testContent->GetParent();
|
|
|
|
if (!testContent) {
|
|
// We run this loop again, checking the ancestor chain of the selection's end point
|
|
testContent = nextTestContent;
|
|
nextTestContent = nullptr;
|
|
}
|
|
}
|
|
|
|
// We couldn't find an anchor that was an ancestor of the selection start
|
|
// Method #2: look for anchor in selection's primary range (depth first search)
|
|
|
|
// Turn into nodes so that we can use GetNextSibling() and GetFirstChild()
|
|
nsCOMPtr<nsIDOMNode> selectionNode(do_QueryInterface(aStartSelection));
|
|
nsCOMPtr<nsIDOMNode> endSelectionNode(do_QueryInterface(aEndSelection));
|
|
nsCOMPtr<nsIDOMNode> testNode;
|
|
|
|
do {
|
|
testContent = do_QueryInterface(selectionNode);
|
|
|
|
// We're looking for any focusable link that could be part of the
|
|
// main document's selection.
|
|
nsCOMPtr<nsIURI> uri;
|
|
if (testContent == currentFocus ||
|
|
testContent->IsLink(getter_AddRefs(uri))) {
|
|
testContent.forget(aFocusedContent);
|
|
return;
|
|
}
|
|
|
|
selectionNode->GetFirstChild(getter_AddRefs(testNode));
|
|
if (testNode) {
|
|
selectionNode = testNode;
|
|
continue;
|
|
}
|
|
|
|
if (selectionNode == endSelectionNode)
|
|
break;
|
|
selectionNode->GetNextSibling(getter_AddRefs(testNode));
|
|
if (testNode) {
|
|
selectionNode = testNode;
|
|
continue;
|
|
}
|
|
|
|
do {
|
|
selectionNode->GetParentNode(getter_AddRefs(testNode));
|
|
if (!testNode || testNode == endSelectionNode) {
|
|
selectionNode = nullptr;
|
|
break;
|
|
}
|
|
testNode->GetNextSibling(getter_AddRefs(selectionNode));
|
|
if (selectionNode)
|
|
break;
|
|
selectionNode = testNode;
|
|
} while (true);
|
|
}
|
|
while (selectionNode && selectionNode != endSelectionNode);
|
|
}
|
|
|
|
class PointerUnlocker : public nsRunnable
|
|
{
|
|
public:
|
|
PointerUnlocker()
|
|
{
|
|
MOZ_ASSERT(!PointerUnlocker::sActiveUnlocker);
|
|
PointerUnlocker::sActiveUnlocker = this;
|
|
}
|
|
|
|
~PointerUnlocker()
|
|
{
|
|
if (PointerUnlocker::sActiveUnlocker == this) {
|
|
PointerUnlocker::sActiveUnlocker = nullptr;
|
|
}
|
|
}
|
|
|
|
NS_IMETHOD Run()
|
|
{
|
|
if (PointerUnlocker::sActiveUnlocker == this) {
|
|
PointerUnlocker::sActiveUnlocker = nullptr;
|
|
}
|
|
NS_ENSURE_STATE(nsFocusManager::GetFocusManager());
|
|
nsPIDOMWindow* focused =
|
|
nsFocusManager::GetFocusManager()->GetFocusedWindow();
|
|
nsCOMPtr<nsIDocument> pointerLockedDoc =
|
|
do_QueryReferent(EventStateManager::sPointerLockedDoc);
|
|
if (pointerLockedDoc &&
|
|
!nsContentUtils::IsInPointerLockContext(focused)) {
|
|
nsIDocument::UnlockPointer();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
static PointerUnlocker* sActiveUnlocker;
|
|
};
|
|
|
|
PointerUnlocker*
|
|
PointerUnlocker::sActiveUnlocker = nullptr;
|
|
|
|
void
|
|
nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindow* aWindow)
|
|
{
|
|
if (!PointerUnlocker::sActiveUnlocker &&
|
|
nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
|
|
!nsContentUtils::IsInPointerLockContext(aWindow)) {
|
|
nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
|
|
NS_DispatchToCurrentThread(runnable);
|
|
}
|
|
mFocusedWindow = aWindow;
|
|
}
|
|
|
|
void
|
|
nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
|
|
{
|
|
if (!sInstance) {
|
|
return;
|
|
}
|
|
|
|
if (sInstance->mActiveWindow) {
|
|
sInstance->mActiveWindow->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mFocusedWindow) {
|
|
sInstance->mFocusedWindow->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mWindowBeingLowered) {
|
|
sInstance->mWindowBeingLowered->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mFocusedContent) {
|
|
sInstance->mFocusedContent->OwnerDoc()->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mFirstBlurEvent) {
|
|
sInstance->mFirstBlurEvent->OwnerDoc()->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mFirstFocusEvent) {
|
|
sInstance->mFirstFocusEvent->OwnerDoc()->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
if (sInstance->mMouseButtonEventHandlingDocument) {
|
|
sInstance->mMouseButtonEventHandlingDocument->
|
|
MarkUncollectableForCCGeneration(aGeneration);
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
NS_NewFocusManager(nsIFocusManager** aResult)
|
|
{
|
|
NS_IF_ADDREF(*aResult = nsFocusManager::GetFocusManager());
|
|
return NS_OK;
|
|
}
|