summaryrefslogtreecommitdiffstats
path: root/dom/base/RangeUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/base/RangeUtils.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/dom/base/RangeUtils.cpp b/dom/base/RangeUtils.cpp
new file mode 100644
index 0000000000..28283054b8
--- /dev/null
+++ b/dom/base/RangeUtils.cpp
@@ -0,0 +1,233 @@
+/* -*- 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/RangeUtils.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/AbstractRange.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/ShadowRoot.h"
+#include "nsContentUtils.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+template bool RangeUtils::IsValidPoints(const RangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary);
+template bool RangeUtils::IsValidPoints(const RangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary);
+template bool RangeUtils::IsValidPoints(const RawRangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary);
+template bool RangeUtils::IsValidPoints(const RawRangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary);
+
+template nsresult RangeUtils::CompareNodeToRangeBoundaries(
+ nsINode* aNode, const RangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange);
+
+template nsresult RangeUtils::CompareNodeToRangeBoundaries(
+ nsINode* aNode, const RangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange);
+
+template nsresult RangeUtils::CompareNodeToRangeBoundaries(
+ nsINode* aNode, const RawRangeBoundary& aStartBoundary,
+ const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange);
+
+template nsresult RangeUtils::CompareNodeToRangeBoundaries(
+ nsINode* aNode, const RawRangeBoundary& aStartBoundary,
+ const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange);
+
+// static
+nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) {
+ if (!aNode) {
+ return nullptr;
+ }
+
+ if (aNode->IsContent()) {
+ if (aNode->NodeInfo()->NameAtom() == nsGkAtoms::documentTypeNodeName) {
+ return nullptr;
+ }
+
+ nsIContent* content = aNode->AsContent();
+
+ // If the node is in a shadow tree then the ShadowRoot is the root.
+ //
+ // FIXME(emilio): Should this be after the NAC check below? We can have NAC
+ // inside Shadow DOM which will peek this path rather than the one below.
+ if (ShadowRoot* containingShadow = content->GetContainingShadow()) {
+ return containingShadow;
+ }
+
+ // If the node is in NAC, then the NAC parent should be the root.
+ if (nsINode* root =
+ content->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
+ return root;
+ }
+ }
+
+ // Elements etc. must be in document or in document fragment,
+ // text nodes in document, in document fragment or in attribute.
+ if (nsINode* root = aNode->GetUncomposedDoc()) {
+ return root;
+ }
+
+ NS_ASSERTION(!aNode->SubtreeRoot()->IsDocument(),
+ "GetUncomposedDoc should have returned a doc");
+
+ // We allow this because of backward compatibility.
+ return aNode->SubtreeRoot();
+}
+
+// static
+template <typename SPT, typename SRT, typename EPT, typename ERT>
+bool RangeUtils::IsValidPoints(
+ const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary) {
+ // Use NS_WARN_IF() only for the cases where the arguments are unexpected.
+ if (NS_WARN_IF(!aStartBoundary.IsSetAndValid()) ||
+ NS_WARN_IF(!aEndBoundary.IsSetAndValid())) {
+ return false;
+ }
+
+ // Otherwise, don't use NS_WARN_IF() for preventing to make console messy.
+ // Instead, check one by one since it is easier to catch the error reason
+ // with debugger.
+
+ if (ComputeRootNode(aStartBoundary.Container()) !=
+ ComputeRootNode(aEndBoundary.Container())) {
+ return false;
+ }
+
+ const Maybe<int32_t> order =
+ nsContentUtils::ComparePoints(aStartBoundary, aEndBoundary);
+ if (!order) {
+ MOZ_ASSERT_UNREACHABLE();
+ return false;
+ }
+
+ return *order != 1;
+}
+
+// static
+Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode,
+ AbstractRange* aAbstractRange) {
+ bool nodeIsBeforeRange{false};
+ bool nodeIsAfterRange{false};
+
+ const nsresult rv = CompareNodeToRange(&aNode, aAbstractRange,
+ &nodeIsBeforeRange, &nodeIsAfterRange);
+ if (NS_FAILED(rv)) {
+ return Nothing();
+ }
+
+ return Some(!nodeIsBeforeRange && !nodeIsAfterRange);
+}
+
+// Utility routine to detect if a content node is completely contained in a
+// range If outNodeBefore is returned true, then the node starts before the
+// range does. If outNodeAfter is returned true, then the node ends after the
+// range does. Note that both of the above might be true. If neither are true,
+// the node is contained inside of the range.
+// XXX - callers responsibility to ensure node in same doc as range!
+
+// static
+nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
+ AbstractRange* aAbstractRange,
+ bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange) {
+ if (NS_WARN_IF(!aAbstractRange) ||
+ NS_WARN_IF(!aAbstractRange->IsPositioned())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ return CompareNodeToRangeBoundaries(aNode, aAbstractRange->StartRef(),
+ aAbstractRange->EndRef(),
+ aNodeIsBeforeRange, aNodeIsAfterRange);
+}
+template <typename SPT, typename SRT, typename EPT, typename ERT>
+nsresult RangeUtils::CompareNodeToRangeBoundaries(
+ nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
+ const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange,
+ bool* aNodeIsAfterRange) {
+ MOZ_ASSERT(aNodeIsBeforeRange);
+ MOZ_ASSERT(aNodeIsAfterRange);
+
+ if (NS_WARN_IF(!aNode) ||
+ NS_WARN_IF(!aStartBoundary.IsSet() || !aEndBoundary.IsSet())) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ // create a pair of dom points that expresses location of node:
+ // NODE(start), NODE(end)
+ // Let incoming range be:
+ // {RANGE(start), RANGE(end)}
+ // if (RANGE(start) <= NODE(start)) and (RANGE(end) => NODE(end))
+ // then the Node is contained (completely) by the Range.
+
+ // gather up the dom point info
+ int32_t nodeStart;
+ uint32_t nodeEnd;
+ nsINode* parent = aNode->GetParentNode();
+ if (!parent) {
+ // can't make a parent/offset pair to represent start or
+ // end of the root node, because it has no parent.
+ // so instead represent it by (node,0) and (node,numChildren)
+ parent = aNode;
+ nodeStart = 0;
+ nodeEnd = aNode->GetChildCount();
+ } else {
+ nodeStart = parent->ComputeIndexOf_Deprecated(aNode);
+ NS_WARNING_ASSERTION(
+ nodeStart >= 0,
+ "aNode has the parent node but it does not have aNode!");
+ nodeEnd = nodeStart + 1u;
+ MOZ_ASSERT(nodeStart < 0 || static_cast<uint32_t>(nodeStart) < nodeEnd,
+ "nodeStart should be less than nodeEnd");
+ }
+
+ // XXX nsContentUtils::ComparePoints() may be expensive. If some callers
+ // just want one of aNodeIsBeforeRange or aNodeIsAfterRange, we can
+ // skip the other comparison.
+
+ // In the ComparePoints calls below we use a container & offset instead of
+ // a range boundary because the range boundary constructor warns if you pass
+ // in a -1 offset and the ComputeIndexOf call above can return -1 if aNode
+ // is native anonymous content. ComparePoints has comments about offsets
+ // being -1 and it seems to deal with it, or at least we aren't aware of any
+ // problems arising because of it. We don't have a better idea how to get
+ // rid of the warning without much larger changes so we do this just to
+ // silence the warning. (Bug 1438996)
+
+ // is RANGE(start) <= NODE(start) ?
+ Maybe<int32_t> order = nsContentUtils::ComparePoints_AllowNegativeOffsets(
+ aStartBoundary.Container(),
+ *aStartBoundary.Offset(
+ RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
+ parent, nodeStart);
+ if (NS_WARN_IF(!order)) {
+ return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
+ }
+ *aNodeIsBeforeRange = *order > 0;
+ // is RANGE(end) >= NODE(end) ?
+ order = nsContentUtils::ComparePoints(
+ aEndBoundary.Container(),
+ *aEndBoundary.Offset(
+ RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOrInvalidOffsets),
+ parent, nodeEnd);
+
+ if (NS_WARN_IF(!order)) {
+ return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
+ }
+ *aNodeIsAfterRange = *order < 0;
+
+ return NS_OK;
+}
+
+} // namespace mozilla