From 940d191ef8b61309f4ea83d0fea77828f361251b Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 17 Apr 2020 05:28:43 -0400 Subject: Bug 1367683 - Optimize initializing nsRange Tag #1375 --- dom/base/nsDocument.cpp | 2 +- dom/base/nsRange.cpp | 121 ++++++++++++++++++++++++++++--------- dom/base/nsRange.h | 57 ++++++++++++++--- dom/events/ContentEventHandler.cpp | 9 +-- 4 files changed, 146 insertions(+), 43 deletions(-) (limited to 'dom') diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp index d0e861b1a..e16c1831c 100644 --- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -6468,7 +6468,7 @@ already_AddRefed nsIDocument::CreateRange(ErrorResult& rv) { RefPtr range = new nsRange(this); - nsresult res = range->Set(this, 0, this, 0); + nsresult res = range->CollapseTo(this, 0); if (NS_FAILED(res)) { rv.Throw(res); return nullptr; diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index d45a2c975..154b3428f 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -270,14 +270,17 @@ nsRange::CreateRange(nsINode* aStartParent, int32_t aStartOffset, nsINode* aEndParent, int32_t aEndOffset, nsRange** aRange) { - nsCOMPtr startDomNode = do_QueryInterface(aStartParent); - nsCOMPtr endDomNode = do_QueryInterface(aEndParent); - - nsresult rv = CreateRange(startDomNode, aStartOffset, endDomNode, aEndOffset, - aRange); - - return rv; + MOZ_ASSERT(aRange); + *aRange = nullptr; + RefPtr range = new nsRange(aStartParent); + nsresult rv = range->SetStartAndEnd(aStartParent, aStartOffset, + aEndParent, aEndOffset); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + range.forget(aRange); + return NS_OK; } /* static */ @@ -286,22 +289,9 @@ nsRange::CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset, nsIDOMNode* aEndParent, int32_t aEndOffset, nsRange** aRange) { - MOZ_ASSERT(aRange); - *aRange = nullptr; - nsCOMPtr startParent = do_QueryInterface(aStartParent); - NS_ENSURE_ARG_POINTER(startParent); - - RefPtr range = new nsRange(startParent); - - nsresult rv = range->SetStart(startParent, aStartOffset); - NS_ENSURE_SUCCESS(rv, rv); - - rv = range->SetEnd(aEndParent, aEndOffset); - NS_ENSURE_SUCCESS(rv, rv); - - range.forget(aRange); - return NS_OK; + nsCOMPtr endParent = do_QueryInterface(aEndParent); + return CreateRange(startParent, aStartOffset, endParent, aEndOffset, aRange); } /* static */ @@ -1137,6 +1127,15 @@ nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent) return rv.StealNSResult(); } +/* static */ +bool +nsRange::IsValidOffset(nsINode* aNode, int32_t aOffset) +{ + return aNode && + aOffset >= 0 && + static_cast(aOffset) <= aNode->Length(); +} + nsINode* nsRange::IsValidBoundary(nsINode* aNode) { @@ -1217,7 +1216,7 @@ nsRange::SetStart(nsINode* aParent, int32_t aOffset) return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; } - if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { + if (!IsValidOffset(aParent, aOffset)) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -1246,7 +1245,9 @@ nsRange::SetStartBefore(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode)); + int32_t offset = -1; + nsINode* parent = GetParentAndOffsetBefore(&aNode, &offset); + aRv = SetStart(parent, offset); } NS_IMETHODIMP @@ -1272,7 +1273,9 @@ nsRange::SetStartAfter(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetStart(aNode.GetParentNode(), IndexOf(&aNode) + 1); + int32_t offset = -1; + nsINode* parent = GetParentAndOffsetAfter(&aNode, &offset); + aRv = SetStart(parent, offset); } NS_IMETHODIMP @@ -1321,7 +1324,7 @@ nsRange::SetEnd(nsINode* aParent, int32_t aOffset) return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; } - if (aOffset < 0 || uint32_t(aOffset) > aParent->Length()) { + if (!IsValidOffset(aParent, aOffset)) { return NS_ERROR_DOM_INDEX_SIZE_ERR; } @@ -1340,6 +1343,64 @@ nsRange::SetEnd(nsINode* aParent, int32_t aOffset) return NS_OK; } +nsresult +nsRange::SetStartAndEnd(nsINode* aStartParent, int32_t aStartOffset, + nsINode* aEndParent, int32_t aEndOffset) +{ + if (NS_WARN_IF(!aStartParent) || NS_WARN_IF(!aEndParent)) { + return NS_ERROR_INVALID_ARG; + } + + nsINode* newStartRoot = IsValidBoundary(aStartParent); + if (!newStartRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!IsValidOffset(aStartParent, aStartOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + if (aStartParent == aEndParent) { + if (!IsValidOffset(aEndParent, aEndOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + // If the end offset is less than the start offset, this should be + // collapsed at the end offset. + if (aStartOffset > aEndOffset) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newStartRoot); + } else { + DoSetRange(aStartParent, aStartOffset, + aEndParent, aEndOffset, newStartRoot); + } + return NS_OK; + } + + nsINode* newEndRoot = IsValidBoundary(aEndParent); + if (!newEndRoot) { + return NS_ERROR_DOM_INVALID_NODE_TYPE_ERR; + } + if (!IsValidOffset(aEndParent, aEndOffset)) { + return NS_ERROR_DOM_INDEX_SIZE_ERR; + } + + // If they have different root, this should be collapsed at the end point. + if (newStartRoot != newEndRoot) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newEndRoot); + return NS_OK; + } + + // If the end point is before the start point, this should be collapsed at + // the end point. + if (nsContentUtils::ComparePoints(aStartParent, aStartOffset, + aEndParent, aEndOffset) == 1) { + DoSetRange(aEndParent, aEndOffset, aEndParent, aEndOffset, newEndRoot); + return NS_OK; + } + + // Otherwise, set the range as specified. + DoSetRange(aStartParent, aStartOffset, aEndParent, aEndOffset, newStartRoot); + return NS_OK; +} + void nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) { @@ -1350,7 +1411,9 @@ nsRange::SetEndBefore(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode)); + int32_t offset = -1; + nsINode* parent = GetParentAndOffsetBefore(&aNode, &offset); + aRv = SetEnd(parent, offset); } NS_IMETHODIMP @@ -1376,7 +1439,9 @@ nsRange::SetEndAfter(nsINode& aNode, ErrorResult& aRv) } AutoInvalidateSelection atEndOfBlock(this); - aRv = SetEnd(aNode.GetParentNode(), IndexOf(&aNode) + 1); + int32_t offset = -1; + nsINode* parent = GetParentAndOffsetAfter(&aNode, &offset); + aRv = SetEnd(parent, offset); } NS_IMETHODIMP diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 4b35c749a..70911338d 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -148,19 +148,61 @@ public: nsINode* GetCommonAncestor() const; void Reset(); + + /** + * SetStart() and SetEnd() sets start point or end point separately. + * However, this is expensive especially when it's a range of Selection. + * When you set both start and end of a range, you should use + * SetStartAndEnd() instead. + */ nsresult SetStart(nsINode* aParent, int32_t aOffset); nsresult SetEnd(nsINode* aParent, int32_t aOffset); + already_AddRefed CloneRange() const; - nsresult Set(nsINode* aStartParent, int32_t aStartOffset, - nsINode* aEndParent, int32_t aEndOffset) + /** + * SetStartAndEnd() works similar to call both SetStart() and SetEnd(). + * Different from calls them separately, this does nothing if either + * the start point or the end point is invalid point. + * If the specified start point is after the end point, the range will be + * collapsed at the end point. Similarly, if they are in different root, + * the range will be collapsed at the end point. + */ + nsresult SetStartAndEnd(nsINode* aStartParent, int32_t aStartOffset, + nsINode* aEndParent, int32_t aEndOffset); + + /** + * CollapseTo() works similar to call both SetStart() and SetEnd() with + * same node and offset. This just calls SetStartAndParent() to set + * collapsed range at aParent and aOffset. + */ + nsresult CollapseTo(nsINode* aParent, int32_t aOffset) { - // If this starts being hot, we may be able to optimize this a bit, - // but for now just set start and end separately. - nsresult rv = SetStart(aStartParent, aStartOffset); - NS_ENSURE_SUCCESS(rv, rv); + return SetStartAndEnd(aParent, aOffset, aParent, aOffset); + } - return SetEnd(aEndParent, aEndOffset); + /** + * Retrieves node and offset for setting start or end of a range to + * before or after aNode. + */ + static nsINode* GetParentAndOffsetAfter(nsINode* aNode, int32_t* aOffset) + { + MOZ_ASSERT(aNode); + MOZ_ASSERT(aOffset); + nsINode* parentNode = aNode->GetParentNode(); + *aOffset = parentNode ? parentNode->IndexOf(aNode) : -1; + if (*aOffset >= 0) { + (*aOffset)++; + } + return parentNode; + } + static nsINode* GetParentAndOffsetBefore(nsINode* aNode, int32_t* aOffset) + { + MOZ_ASSERT(aNode); + MOZ_ASSERT(aOffset); + nsINode* parentNode = aNode->GetParentNode(); + *aOffset = parentNode ? parentNode->IndexOf(aNode) : -1; + return parentNode; } NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult); @@ -296,6 +338,7 @@ protected: void RegisterCommonAncestor(nsINode* aNode); void UnregisterCommonAncestor(nsINode* aNode); nsINode* IsValidBoundary(nsINode* aNode); + static bool IsValidOffset(nsINode* aNode, int32_t aOffset); // CharacterDataChanged set aNotInsertedYet to true to disable an assertion // and suppress re-registering a range common ancestor node since diff --git a/dom/events/ContentEventHandler.cpp b/dom/events/ContentEventHandler.cpp index 935ade23f..df54a4d44 100644 --- a/dom/events/ContentEventHandler.cpp +++ b/dom/events/ContentEventHandler.cpp @@ -980,11 +980,7 @@ ContentEventHandler::SetRangeFromFlatTextOffset(nsRange* aRange, // Special case like
if (!mRootContent->HasChildren()) { - nsresult rv = aRange->SetStart(mRootContent, 0); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - rv = aRange->SetEnd(mRootContent, 0); + nsresult rv = aRange->CollapseTo(mRootContent, 0); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } @@ -2880,8 +2876,7 @@ ContentEventHandler::AdjustCollapsedRangeMaybeIntoTextNode(nsRange* aRange) return NS_OK; } - nsresult rv = aRange->Set(childNode, offsetInChildNode, - childNode, offsetInChildNode); + nsresult rv = aRange->CollapseTo(childNode, offsetInChildNode); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } -- cgit v1.2.3