Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / base / CrossShadowBoundaryRange.cpp
blob2f61294bdb9cc17e467f24db309b38f22fe51fa0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/dom/CrossShadowBoundaryRange.h"
8 #include "nsContentUtils.h"
9 #include "nsINode.h"
10 #include "nsRange.h"
11 #include "nsIContentInlines.h"
13 namespace mozilla::dom {
14 template already_AddRefed<CrossShadowBoundaryRange>
15 CrossShadowBoundaryRange::Create(const RangeBoundary& aStartBoundary,
16 const RangeBoundary& aEndBoundary,
17 nsRange* aOwner);
18 template already_AddRefed<CrossShadowBoundaryRange>
19 CrossShadowBoundaryRange::Create(const RangeBoundary& aStartBoundary,
20 const RawRangeBoundary& aEndBoundary,
21 nsRange* aOwner);
22 template already_AddRefed<CrossShadowBoundaryRange>
23 CrossShadowBoundaryRange::Create(const RawRangeBoundary& aStartBoundary,
24 const RangeBoundary& aEndBoundary,
25 nsRange* aOwner);
26 template already_AddRefed<CrossShadowBoundaryRange>
27 CrossShadowBoundaryRange::Create(const RawRangeBoundary& aStartBoundary,
28 const RawRangeBoundary& aEndBoundary,
29 nsRange* aOwner);
31 template void CrossShadowBoundaryRange::DoSetRange(
32 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
33 nsINode* aRootNode, nsRange* aOwner);
34 template void CrossShadowBoundaryRange::DoSetRange(
35 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary,
36 nsINode* aRootNode, nsRange* aOwner);
37 template void CrossShadowBoundaryRange::DoSetRange(
38 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary,
39 nsINode* aRootNode, nsRange* aOwner);
40 template void CrossShadowBoundaryRange::DoSetRange(
41 const RawRangeBoundary& aStartBoundary,
42 const RawRangeBoundary& aEndBoundary, nsINode* aRootNode, nsRange* aOwner);
44 template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
45 const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
46 template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
47 const RangeBoundary& aStartBoundary, const RawRangeBoundary& aEndBoundary);
48 template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
49 const RawRangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary);
50 template nsresult CrossShadowBoundaryRange::SetStartAndEnd(
51 const RawRangeBoundary& aStartBoundary,
52 const RawRangeBoundary& aEndBoundary);
54 nsTArray<RefPtr<CrossShadowBoundaryRange>>*
55 CrossShadowBoundaryRange::sCachedRanges = nullptr;
57 NS_IMPL_CYCLE_COLLECTING_ADDREF(CrossShadowBoundaryRange)
59 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_INTERRUPTABLE_LAST_RELEASE(
60 CrossShadowBoundaryRange,
61 DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr, nullptr),
62 AbstractRange::MaybeCacheToReuse(*this))
64 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CrossShadowBoundaryRange)
65 NS_INTERFACE_MAP_END_INHERITING(CrossShadowBoundaryRange)
67 NS_IMPL_CYCLE_COLLECTION_CLASS(CrossShadowBoundaryRange)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(CrossShadowBoundaryRange,
70 StaticRange)
71 if (tmp->mCommonAncestor) {
72 tmp->mCommonAncestor->RemoveMutationObserver(tmp);
74 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommonAncestor)
75 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
77 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(CrossShadowBoundaryRange,
78 StaticRange)
79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommonAncestor)
80 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
82 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(CrossShadowBoundaryRange,
83 StaticRange)
84 NS_IMPL_CYCLE_COLLECTION_TRACE_END
86 /* static */
87 template <typename SPT, typename SRT, typename EPT, typename ERT>
88 already_AddRefed<CrossShadowBoundaryRange> CrossShadowBoundaryRange::Create(
89 const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
90 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, nsRange* aOwner) {
91 RefPtr<CrossShadowBoundaryRange> range;
92 if (!sCachedRanges || sCachedRanges->IsEmpty()) {
93 range = new CrossShadowBoundaryRange(aStartBoundary.Container(), aOwner);
94 } else {
95 range = sCachedRanges->PopLastElement().forget();
98 range->Init(aStartBoundary.Container());
99 range->DoSetRange(aStartBoundary, aEndBoundary, nullptr, aOwner);
100 return range.forget();
103 template <typename SPT, typename SRT, typename EPT, typename ERT>
104 void CrossShadowBoundaryRange::DoSetRange(
105 const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
106 const RangeBoundaryBase<EPT, ERT>& aEndBoundary, nsINode* aRootNode,
107 nsRange* aOwner) {
108 // aRootNode is useless to CrossShadowBoundaryRange because aStartBoundary
109 // and aEndBoundary could have different roots.
110 StaticRange::DoSetRange(aStartBoundary, aEndBoundary, nullptr);
112 nsINode* startRoot = RangeUtils::ComputeRootNode(mStart.Container());
113 nsINode* endRoot = RangeUtils::ComputeRootNode(mEnd.Container());
115 nsINode* previousCommonAncestor = mCommonAncestor;
116 if (startRoot == endRoot) {
117 MOZ_ASSERT(!startRoot && !endRoot);
118 MOZ_ASSERT(!aOwner);
119 // This should be the case when Release() is called.
120 mCommonAncestor = startRoot;
121 mOwner = nullptr;
122 } else {
123 mCommonAncestor =
124 nsContentUtils::GetClosestCommonShadowIncludingInclusiveAncestor(
125 mStart.Container(), mEnd.Container());
126 MOZ_ASSERT_IF(mOwner, mOwner == aOwner);
127 if (!mOwner) {
128 mOwner = aOwner;
132 if (previousCommonAncestor != mCommonAncestor) {
133 if (previousCommonAncestor) {
134 previousCommonAncestor->RemoveMutationObserver(this);
136 if (mCommonAncestor) {
137 mCommonAncestor->AddMutationObserver(this);
141 void CrossShadowBoundaryRange::ContentWillBeRemoved(nsIContent* aChild,
142 const BatchRemovalState*) {
143 // It's unclear from the spec about what should the selection be after
144 // DOM mutation. See https://github.com/w3c/selection-api/issues/168
146 // For now, we just clear the selection if the removed node is related
147 // to mStart or mEnd.
148 MOZ_DIAGNOSTIC_ASSERT(mOwner);
149 MOZ_DIAGNOSTIC_ASSERT(mOwner->GetCrossShadowBoundaryRange() == this);
151 RefPtr<CrossShadowBoundaryRange> kungFuDeathGrip(this);
153 const nsINode* startContainer = mStart.Container();
154 const nsINode* endContainer = mEnd.Container();
156 if (startContainer == aChild || endContainer == aChild) {
157 mOwner->ResetCrossShadowBoundaryRange();
158 return;
161 if (const auto* shadowRoot = aChild->GetShadowRoot()) {
162 if (startContainer == shadowRoot || endContainer == shadowRoot) {
163 mOwner->ResetCrossShadowBoundaryRange();
164 return;
168 if (mStart.Container()->IsShadowIncludingInclusiveDescendantOf(aChild) ||
169 mEnd.Container()->IsShadowIncludingInclusiveDescendantOf(aChild)) {
170 mOwner->ResetCrossShadowBoundaryRange();
171 return;
174 nsINode* container = aChild->GetParentNode();
176 auto MaybeCreateNewBoundary =
177 [container, aChild](
178 const nsINode* aContainer,
179 const RangeBoundary& aBoundary) -> Maybe<RawRangeBoundary> {
180 if (container == aContainer) {
181 // We're only interested if our boundary reference was removed, otherwise
182 // we can just invalidate the offset.
183 if (aChild == aBoundary.Ref()) {
184 return Some<RawRangeBoundary>(
185 {container, aChild->GetPreviousSibling()});
187 RawRangeBoundary newBoundary;
188 newBoundary.CopyFrom(aBoundary, RangeBoundaryIsMutationObserved::Yes);
189 newBoundary.InvalidateOffset();
190 return Some(newBoundary);
192 return Nothing();
195 const Maybe<RawRangeBoundary> newStartBoundary =
196 MaybeCreateNewBoundary(startContainer, mStart);
197 const Maybe<RawRangeBoundary> newEndBoundary =
198 MaybeCreateNewBoundary(endContainer, mEnd);
200 if (newStartBoundary || newEndBoundary) {
201 SetStartAndEnd(newStartBoundary ? newStartBoundary.ref() : mStart.AsRaw(),
202 newEndBoundary ? newEndBoundary.ref() : mEnd.AsRaw());
206 // For now CrossShadowBoundaryRange::CharacterDataChanged is only meant
207 // to handle the character removal initiated by nsRange::CutContents.
208 void CrossShadowBoundaryRange::CharacterDataChanged(
209 nsIContent* aContent, const CharacterDataChangeInfo& aInfo) {
210 // When aInfo.mDetails is present, it means the character data was
211 // changed due to splitText() or normalize(), which shouldn't be the
212 // case for nsRange::CutContents, so we return early.
213 if (aInfo.mDetails) {
214 return;
216 MOZ_ASSERT(aContent);
217 MOZ_ASSERT(mIsPositioned);
219 auto MaybeCreateNewBoundary =
220 [aContent,
221 &aInfo](const RangeBoundary& aBoundary) -> Maybe<RawRangeBoundary> {
222 // If the changed node contains our start boundary and the change starts
223 // before the boundary we'll need to adjust the offset.
224 if (aContent == aBoundary.Container() &&
225 // aInfo.mChangeStart is the offset where the change starts, if it's
226 // smaller than the offset of aBoundary, it means the characters
227 // before the selected content is changed (i.e, removed), so the
228 // offset of aBoundary needs to be adjusted.
229 aInfo.mChangeStart <
230 *aBoundary.Offset(
231 RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)) {
232 RawRangeBoundary newStart =
233 nsRange::ComputeNewBoundaryWhenBoundaryInsideChangedText(
234 aInfo, aBoundary.AsRaw());
235 return Some(newStart);
237 return Nothing();
240 const Maybe<RawRangeBoundary> newStartBoundary =
241 MaybeCreateNewBoundary(mStart);
242 const Maybe<RawRangeBoundary> newEndBoundary = MaybeCreateNewBoundary(mEnd);
244 if (newStartBoundary || newEndBoundary) {
245 DoSetRange(newStartBoundary ? newStartBoundary.ref() : mStart.AsRaw(),
246 newEndBoundary ? newEndBoundary.ref() : mEnd.AsRaw(), nullptr,
247 mOwner);
251 // DOM mutation for shadow-crossing selection is not specified.
252 // Spec issue: https://github.com/w3c/selection-api/issues/168
253 void CrossShadowBoundaryRange::ParentChainChanged(nsIContent* aContent) {
254 MOZ_DIAGNOSTIC_ASSERT(mCommonAncestor == aContent,
255 "Wrong ParentChainChanged notification");
256 MOZ_DIAGNOSTIC_ASSERT(mOwner);
257 mOwner->ResetCrossShadowBoundaryRange();
259 } // namespace mozilla::dom