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/RangeUtils.h"
9 #include "mozilla/Assertions.h"
10 #include "mozilla/dom/AbstractRange.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/ShadowRoot.h"
13 #include "nsContentUtils.h"
19 template bool RangeUtils::IsValidPoints(const RangeBoundary
& aStartBoundary
,
20 const RangeBoundary
& aEndBoundary
);
21 template bool RangeUtils::IsValidPoints(const RangeBoundary
& aStartBoundary
,
22 const RawRangeBoundary
& aEndBoundary
);
23 template bool RangeUtils::IsValidPoints(const RawRangeBoundary
& aStartBoundary
,
24 const RangeBoundary
& aEndBoundary
);
25 template bool RangeUtils::IsValidPoints(const RawRangeBoundary
& aStartBoundary
,
26 const RawRangeBoundary
& aEndBoundary
);
28 template nsresult
RangeUtils::CompareNodeToRangeBoundaries(
29 nsINode
* aNode
, const RangeBoundary
& aStartBoundary
,
30 const RangeBoundary
& aEndBoundary
, bool* aNodeIsBeforeRange
,
31 bool* aNodeIsAfterRange
);
33 template nsresult
RangeUtils::CompareNodeToRangeBoundaries(
34 nsINode
* aNode
, const RangeBoundary
& aStartBoundary
,
35 const RawRangeBoundary
& aEndBoundary
, bool* aNodeIsBeforeRange
,
36 bool* aNodeIsAfterRange
);
38 template nsresult
RangeUtils::CompareNodeToRangeBoundaries(
39 nsINode
* aNode
, const RawRangeBoundary
& aStartBoundary
,
40 const RangeBoundary
& aEndBoundary
, bool* aNodeIsBeforeRange
,
41 bool* aNodeIsAfterRange
);
43 template nsresult
RangeUtils::CompareNodeToRangeBoundaries(
44 nsINode
* aNode
, const RawRangeBoundary
& aStartBoundary
,
45 const RawRangeBoundary
& aEndBoundary
, bool* aNodeIsBeforeRange
,
46 bool* aNodeIsAfterRange
);
49 nsINode
* RangeUtils::ComputeRootNode(nsINode
* aNode
) {
54 if (aNode
->IsContent()) {
55 if (aNode
->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName
) {
59 nsIContent
* content
= aNode
->AsContent();
61 // If the node is in a shadow tree then the ShadowRoot is the root.
63 // FIXME(emilio): Should this be after the NAC check below? We can have NAC
64 // inside Shadow DOM which will peek this path rather than the one below.
65 if (ShadowRoot
* containingShadow
= content
->GetContainingShadow()) {
66 return containingShadow
;
69 // If the node is in NAC, then the NAC parent should be the root.
71 content
->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
76 // Elements etc. must be in document or in document fragment,
77 // text nodes in document, in document fragment or in attribute.
78 if (nsINode
* root
= aNode
->GetUncomposedDoc()) {
82 NS_ASSERTION(!aNode
->SubtreeRoot()->IsDocument(),
83 "GetUncomposedDoc should have returned a doc");
85 // We allow this because of backward compatibility.
86 return aNode
->SubtreeRoot();
90 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
91 bool RangeUtils::IsValidPoints(
92 const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
93 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
) {
94 // Use NS_WARN_IF() only for the cases where the arguments are unexpected.
95 if (NS_WARN_IF(!aStartBoundary
.IsSetAndValid()) ||
96 NS_WARN_IF(!aEndBoundary
.IsSetAndValid())) {
100 // Otherwise, don't use NS_WARN_IF() for preventing to make console messy.
101 // Instead, check one by one since it is easier to catch the error reason
104 if (ComputeRootNode(aStartBoundary
.Container()) !=
105 ComputeRootNode(aEndBoundary
.Container())) {
109 const Maybe
<int32_t> order
=
110 nsContentUtils::ComparePoints(aStartBoundary
, aEndBoundary
);
112 MOZ_ASSERT_UNREACHABLE();
120 Maybe
<bool> RangeUtils::IsNodeContainedInRange(nsINode
& aNode
,
121 AbstractRange
* aAbstractRange
) {
122 bool nodeIsBeforeRange
{false};
123 bool nodeIsAfterRange
{false};
125 const nsresult rv
= CompareNodeToRange(&aNode
, aAbstractRange
,
126 &nodeIsBeforeRange
, &nodeIsAfterRange
);
131 return Some(!nodeIsBeforeRange
&& !nodeIsAfterRange
);
134 // Utility routine to detect if a content node is completely contained in a
135 // range If outNodeBefore is returned true, then the node starts before the
136 // range does. If outNodeAfter is returned true, then the node ends after the
137 // range does. Note that both of the above might be true. If neither are true,
138 // the node is contained inside of the range.
139 // XXX - callers responsibility to ensure node in same doc as range!
142 nsresult
RangeUtils::CompareNodeToRange(nsINode
* aNode
,
143 AbstractRange
* aAbstractRange
,
144 bool* aNodeIsBeforeRange
,
145 bool* aNodeIsAfterRange
) {
146 if (NS_WARN_IF(!aAbstractRange
) ||
147 NS_WARN_IF(!aAbstractRange
->IsPositioned())) {
148 return NS_ERROR_INVALID_ARG
;
150 return CompareNodeToRangeBoundaries(aNode
, aAbstractRange
->StartRef(),
151 aAbstractRange
->EndRef(),
152 aNodeIsBeforeRange
, aNodeIsAfterRange
);
154 template <typename SPT
, typename SRT
, typename EPT
, typename ERT
>
155 nsresult
RangeUtils::CompareNodeToRangeBoundaries(
156 nsINode
* aNode
, const RangeBoundaryBase
<SPT
, SRT
>& aStartBoundary
,
157 const RangeBoundaryBase
<EPT
, ERT
>& aEndBoundary
, bool* aNodeIsBeforeRange
,
158 bool* aNodeIsAfterRange
) {
159 MOZ_ASSERT(aNodeIsBeforeRange
);
160 MOZ_ASSERT(aNodeIsAfterRange
);
162 if (NS_WARN_IF(!aNode
) ||
163 NS_WARN_IF(!aStartBoundary
.IsSet() || !aEndBoundary
.IsSet())) {
164 return NS_ERROR_INVALID_ARG
;
167 // create a pair of dom points that expresses location of node:
168 // NODE(start), NODE(end)
169 // Let incoming range be:
170 // {RANGE(start), RANGE(end)}
171 // if (RANGE(start) <= NODE(start)) and (RANGE(end) => NODE(end))
172 // then the Node is contained (completely) by the Range.
174 // gather up the dom point info
177 nsINode
* parent
= aNode
->GetParentNode();
179 // can't make a parent/offset pair to represent start or
180 // end of the root node, because it has no parent.
181 // so instead represent it by (node,0) and (node,numChildren)
184 nodeEnd
= aNode
->GetChildCount();
186 nodeStart
= parent
->ComputeIndexOf_Deprecated(aNode
);
187 NS_WARNING_ASSERTION(
189 "aNode has the parent node but it does not have aNode!");
190 nodeEnd
= nodeStart
+ 1u;
191 MOZ_ASSERT(nodeStart
< 0 || static_cast<uint32_t>(nodeStart
) < nodeEnd
,
192 "nodeStart should be less than nodeEnd");
195 // XXX nsContentUtils::ComparePoints() may be expensive. If some callers
196 // just want one of aNodeIsBeforeRange or aNodeIsAfterRange, we can
197 // skip the other comparison.
199 // In the ComparePoints calls below we use a container & offset instead of
200 // a range boundary because the range boundary constructor warns if you pass
201 // in a -1 offset and the ComputeIndexOf call above can return -1 if aNode
202 // is native anonymous content. ComparePoints has comments about offsets
203 // being -1 and it seems to deal with it, or at least we aren't aware of any
204 // problems arising because of it. We don't have a better idea how to get
205 // rid of the warning without much larger changes so we do this just to
206 // silence the warning. (Bug 1438996)
208 // is RANGE(start) <= NODE(start) ?
209 Maybe
<int32_t> order
= nsContentUtils::ComparePoints_AllowNegativeOffsets(
210 aStartBoundary
.Container(),
211 *aStartBoundary
.Offset(
212 RangeBoundaryBase
<SPT
, SRT
>::OffsetFilter::kValidOrInvalidOffsets
),
214 if (NS_WARN_IF(!order
)) {
215 return NS_ERROR_DOM_WRONG_DOCUMENT_ERR
;
217 *aNodeIsBeforeRange
= *order
> 0;
218 // is RANGE(end) >= NODE(end) ?
219 order
= nsContentUtils::ComparePoints(
220 aEndBoundary
.Container(),
221 *aEndBoundary
.Offset(
222 RangeBoundaryBase
<EPT
, ERT
>::OffsetFilter::kValidOrInvalidOffsets
),
225 if (NS_WARN_IF(!order
)) {
226 return NS_ERROR_DOM_WRONG_DOCUMENT_ERR
;
228 *aNodeIsAfterRange
= *order
< 0;
233 } // namespace mozilla