Bug 1852740: add tests for the `fetchpriority` attribute in Link headers. r=necko...
[gecko.git] / dom / base / RangeUtils.cpp
blob28283054b88ee747976ae4331e0e664cead653f4
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"
15 namespace mozilla {
17 using namespace dom;
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);
48 // static
49 nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) {
50 if (!aNode) {
51 return nullptr;
54 if (aNode->IsContent()) {
55 if (aNode->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
56 return nullptr;
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.
70 if (nsINode* root =
71 content->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
72 return root;
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()) {
79 return root;
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();
89 // static
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())) {
97 return false;
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
102 // with debugger.
104 if (ComputeRootNode(aStartBoundary.Container()) !=
105 ComputeRootNode(aEndBoundary.Container())) {
106 return false;
109 const Maybe<int32_t> order =
110 nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary);
111 if (!order) {
112 MOZ_ASSERT_UNREACHABLE();
113 return false;
116 return *order != 1;
119 // static
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);
127 if (NS_FAILED(rv)) {
128 return Nothing();
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!
141 // static
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
175 int32_t nodeStart;
176 uint32_t nodeEnd;
177 nsINode* parent = aNode->GetParentNode();
178 if (!parent) {
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)
182 parent = aNode;
183 nodeStart = 0;
184 nodeEnd = aNode->GetChildCount();
185 } else {
186 nodeStart = parent->ComputeIndexOf_Deprecated(aNode);
187 NS_WARNING_ASSERTION(
188 nodeStart >= 0,
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),
213 parent, nodeStart);
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),
223 parent, nodeEnd);
225 if (NS_WARN_IF(!order)) {
226 return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
228 *aNodeIsAfterRange = *order < 0;
230 return NS_OK;
233 } // namespace mozilla