diff --git a/dom/events/IMEContentObserver.cpp b/dom/events/IMEContentObserver.cpp
index 29030490150b..520783642ed5 100644
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -48,6 +48,29 @@ ToChar(bool aBool)
return aBool ? "true" : "false";
}
+// This method determines the node to use for the point before the current node.
+// If you have the following aContent and aContainer, and want to represent the
+// following point for `NodePosition` or `RangeBoundary`:
+//
+// {node} {node} | {node}
+// ^ ^ ^
+// aContainer point aContent
+//
+// This function will shift `aContent` to the left into the format which
+// `NodePosition` and `RangeBoundary` use:
+//
+// {node} {node} | {node}
+// ^ ^ ^
+// aContainer result point
+static nsIContent*
+PointBefore(nsINode* aContainer, nsIContent* aContent)
+{
+ if (aContent) {
+ return aContent->GetPreviousSibling();
+ }
+ return aContainer->GetLastChild();
+}
+
class WritingModeToString final : public nsAutoCString
{
public:
@@ -123,8 +146,8 @@ public:
NS_IMPL_CYCLE_COLLECTION_CLASS(IMEContentObserver)
-// Note that we don't need to add mFirstAddedNodeContainer nor
-// mLastAddedNodeContainer to cycle collection because they are non-null only
+// Note that we don't need to add mFirstAddedContainer nor
+// mLastAddedContainer to cycle collection because they are non-null only
// during short time and shouldn't be touched while they are non-null.
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IMEContentObserver)
@@ -174,9 +197,7 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
IMEContentObserver::IMEContentObserver()
- : mFirstAddedNodeOffset(0)
- , mLastAddedNodeOffset(0)
- , mESM(nullptr)
+ : mESM(nullptr)
, mIMENotificationRequests(nullptr)
, mSuppressNotifications(0)
, mPreCharacterDataChangeLength(-1)
@@ -1001,13 +1022,16 @@ IMEContentObserver::CharacterDataChanged(nsIDocument* aDocument,
void
IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
- int32_t aStartIndex,
- int32_t aEndIndex)
+ nsIContent* aFirstContent,
+ nsIContent* aLastContent)
{
if (!NeedsTextChangeNotification()) {
return;
}
+ MOZ_ASSERT_IF(aFirstContent, aFirstContent->GetParentNode() == aContainer);
+ MOZ_ASSERT_IF(aLastContent, aLastContent->GetParentNode() == aContainer);
+
mStartOfRemovingTextRangeCache.Clear();
// If it's in a document change, nodes are added consecutively. Therefore,
@@ -1019,9 +1043,9 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// the last node in mEndOfAddedTextCache. Clear it.
mEndOfAddedTextCache.Clear();
if (!HasAddedNodesDuringDocumentChange()) {
- mFirstAddedNodeContainer = mLastAddedNodeContainer = aContainer;
- mFirstAddedNodeOffset = aStartIndex;
- mLastAddedNodeOffset = aEndIndex;
+ mFirstAddedContainer = mLastAddedContainer = aContainer;
+ mFirstAddedContent = aFirstContent;
+ mLastAddedContent = aLastContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this));
@@ -1030,17 +1054,17 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// If first node being added is not next node of the last node,
// notify IME of the previous range first, then, restart to cache the
// range.
- if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aStartIndex))) {
+ if (NS_WARN_IF(!IsNextNodeOfLastAddedNode(aContainer, aFirstContent))) {
// Flush the old range first.
MaybeNotifyIMEOfAddedTextDuringDocumentChange();
- mFirstAddedNodeContainer = aContainer;
- mFirstAddedNodeOffset = aStartIndex;
+ mFirstAddedContainer = aContainer;
+ mFirstAddedContent = aFirstContent;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::NotifyContentAdded(), starts to store "
"consecutive added nodes", this));
}
- mLastAddedNodeContainer = aContainer;
- mLastAddedNodeOffset = aEndIndex;
+ mLastAddedContainer = aContainer;
+ mLastAddedContent = aLastContent;
return;
}
MOZ_ASSERT(!HasAddedNodesDuringDocumentChange(),
@@ -1048,11 +1072,14 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
uint32_t offset = 0;
nsresult rv = NS_OK;
- if (!mEndOfAddedTextCache.Match(aContainer, aStartIndex)) {
+ if (!mEndOfAddedTextCache.Match(aContainer,
+ aFirstContent->GetPreviousSibling())) {
mEndOfAddedTextCache.Clear();
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
- NodePositionBefore(aContainer, aStartIndex),
+ NodePositionBefore(aContainer,
+ PointBefore(aContainer,
+ aFirstContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
return;
@@ -1064,8 +1091,10 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// get offset at the end of the last added node
uint32_t addingLength = 0;
rv = ContentEventHandler::GetFlatTextLengthInRange(
- NodePositionBefore(aContainer, aStartIndex),
- NodePosition(aContainer, aEndIndex),
+ NodePositionBefore(aContainer,
+ PointBefore(aContainer,
+ aFirstContent)),
+ NodePosition(aContainer, aLastContent),
mRootContent, &addingLength,
LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED((rv)))) {
@@ -1077,7 +1106,7 @@ IMEContentObserver::NotifyContentAdded(nsINode* aContainer,
// NotifyContentAdded() is for adding next node. Therefore, caching the text
// length can skip to compute the text length before the adding node and
// before of it.
- mEndOfAddedTextCache.Cache(aContainer, aEndIndex, offset + addingLength);
+ mEndOfAddedTextCache.Cache(aContainer, aLastContent, offset + addingLength);
if (!addingLength) {
return;
@@ -1093,27 +1122,27 @@ void
IMEContentObserver::ContentAppended(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aFirstNewContent,
- int32_t aNewIndexInContainer)
+ int32_t /* unused */)
{
- NotifyContentAdded(aContainer, aNewIndexInContainer,
- aContainer->GetChildCount());
+ NotifyContentAdded(NODE_FROM(aContainer, aDocument),
+ aFirstNewContent, aContainer->GetLastChild());
}
void
IMEContentObserver::ContentInserted(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
- int32_t aIndexInContainer)
+ int32_t /* unused */)
{
NotifyContentAdded(NODE_FROM(aContainer, aDocument),
- aIndexInContainer, aIndexInContainer + 1);
+ aChild, aChild);
}
void
IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
nsIContent* aContainer,
nsIContent* aChild,
- int32_t aIndexInContainer,
+ int32_t /* unused */,
nsIContent* aPreviousSibling)
{
if (!NeedsTextChangeNotification()) {
@@ -1127,18 +1156,19 @@ IMEContentObserver::ContentRemoved(nsIDocument* aDocument,
uint32_t offset = 0;
nsresult rv = NS_OK;
- if (!mStartOfRemovingTextRangeCache.Match(containerNode, aIndexInContainer)) {
+ if (!mStartOfRemovingTextRangeCache.Match(containerNode, aPreviousSibling)) {
// At removing a child node of aContainer, we need the line break caused
- // by open tag of aContainer. Be careful when aIndexInContainer is 0.
+ // by open tag of aContainer. Be careful when aPreviousSibling is nullptr.
+
rv = ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
- NodePosition(containerNode, aIndexInContainer),
+ NodePosition(containerNode, aPreviousSibling),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
mStartOfRemovingTextRangeCache.Clear();
return;
}
- mStartOfRemovingTextRangeCache.Cache(containerNode, aIndexInContainer,
+ mStartOfRemovingTextRangeCache.Cache(containerNode, aPreviousSibling,
offset);
} else {
offset = mStartOfRemovingTextRangeCache.mFlatTextLength;
@@ -1230,8 +1260,8 @@ IMEContentObserver::AttributeChanged(nsIDocument* aDocument,
void
IMEContentObserver::ClearAddedNodesDuringDocumentChange()
{
- mFirstAddedNodeContainer = mLastAddedNodeContainer = nullptr;
- mFirstAddedNodeOffset = mLastAddedNodeOffset = 0;
+ mFirstAddedContainer = mLastAddedContainer = nullptr;
+ mFirstAddedContent = mLastAddedContent = nullptr;
MOZ_LOG(sIMECOLog, LogLevel::Debug,
("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
", finished storing consecutive nodes", this));
@@ -1256,66 +1286,46 @@ IMEContentObserver::GetChildNode(nsINode* aParent, int32_t aOffset)
bool
IMEContentObserver::IsNextNodeOfLastAddedNode(nsINode* aParent,
- int32_t aOffset) const
+ nsIContent* aChild) const
{
MOZ_ASSERT(aParent);
- MOZ_ASSERT(aOffset >= 0 &&
- aOffset <= static_cast(aParent->Length()));
+ MOZ_ASSERT(aChild && aChild->GetParentNode() == aParent);
MOZ_ASSERT(mRootContent);
MOZ_ASSERT(HasAddedNodesDuringDocumentChange());
- // If the parent node isn't changed, we can check it only with offset.
- if (aParent == mLastAddedNodeContainer) {
- if (NS_WARN_IF(mLastAddedNodeOffset != aOffset)) {
+ // If the parent node isn't changed, we can check that mLastAddedContent has
+ // aChild as its next sibling.
+ if (aParent == mLastAddedContainer) {
+ if (NS_WARN_IF(mLastAddedContent->GetNextSibling() != aChild)) {
return false;
}
return true;
}
- // If the parent node is changed, that means that given offset should be the
- // last added node not having next sibling.
- if (NS_WARN_IF(mLastAddedNodeOffset !=
- static_cast(mLastAddedNodeContainer->Length()))) {
+ // If the parent node is changed, that means that the recorded last added node
+ // shouldn't have a sibling.
+ if (NS_WARN_IF(mLastAddedContent->GetNextSibling())) {
return false;
}
- // If the node is aParent is a descendant of mLastAddedNodeContainer,
- // aOffset should be 0.
- if (mLastAddedNodeContainer == aParent->GetParent()) {
- if (NS_WARN_IF(aOffset)) {
+ // If the node is aParent is a descendant of mLastAddedContainer,
+ // aChild should be the first child in the new container.
+ if (mLastAddedContainer == aParent->GetParent()) {
+ if (NS_WARN_IF(aChild->GetPreviousSibling())) {
return false;
}
return true;
}
// Otherwise, we need to check it even with slow path.
- nsIContent* lastAddedContent =
- GetChildNode(mLastAddedNodeContainer, mLastAddedNodeOffset - 1);
- if (NS_WARN_IF(!lastAddedContent)) {
- return false;
- }
-
nsIContent* nextContentOfLastAddedContent =
- lastAddedContent->GetNextNode(mRootContent->GetParentNode());
+ mLastAddedContent->GetNextNode(mRootContent->GetParentNode());
if (NS_WARN_IF(!nextContentOfLastAddedContent)) {
return false;
}
-
- nsIContent* startContent = GetChildNode(aParent, aOffset);
- if (NS_WARN_IF(!startContent) ||
- NS_WARN_IF(nextContentOfLastAddedContent != startContent)) {
+ if (NS_WARN_IF(nextContentOfLastAddedContent != aChild)) {
return false;
}
-#ifdef DEBUG
- NS_WARNING_ASSERTION(
- !aOffset || aOffset == static_cast(aParent->Length() - 1),
- "Used slow path for aParent");
- NS_WARNING_ASSERTION(
- !(mLastAddedNodeOffset - 1) ||
- mLastAddedNodeOffset ==
- static_cast(mLastAddedNodeContainer->Length()),
- "Used slow path for mLastAddedNodeContainer");
-#endif // #ifdef DEBUG
return true;
}
@@ -1338,8 +1348,9 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
nsresult rv =
ContentEventHandler::GetFlatTextLengthInRange(
NodePosition(mRootContent, 0),
- NodePosition(mFirstAddedNodeContainer,
- mFirstAddedNodeOffset),
+ NodePosition(mFirstAddedContainer,
+ PointBefore(mFirstAddedContainer,
+ mFirstAddedContent)),
mRootContent, &offset, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange();
@@ -1350,10 +1361,10 @@ IMEContentObserver::MaybeNotifyIMEOfAddedTextDuringDocumentChange()
uint32_t length;
rv =
ContentEventHandler::GetFlatTextLengthInRange(
- NodePosition(mFirstAddedNodeContainer,
- mFirstAddedNodeOffset),
- NodePosition(mLastAddedNodeContainer,
- mLastAddedNodeOffset),
+ NodePosition(mFirstAddedContainer,
+ PointBefore(mFirstAddedContainer,
+ mFirstAddedContent)),
+ NodePosition(mLastAddedContainer, mLastAddedContent),
mRootContent, &length, LINE_BREAK_TYPE_NATIVE);
if (NS_WARN_IF(NS_FAILED(rv))) {
ClearAddedNodesDuringDocumentChange();
diff --git a/dom/events/IMEContentObserver.h b/dom/events/IMEContentObserver.h
index 6b24606a84cc..0822012a9b29 100644
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -202,9 +202,9 @@ private:
/**
* MaybeNotifyIMEOfAddedTextDuringDocumentChange() may send text change
- * notification caused by the nodes added between mFirstAddedNodeOffset in
- * mFirstAddedNodeContainer and mLastAddedNodeOffset in
- * mLastAddedNodeContainer and forgets the range.
+ * notification caused by the nodes added between mFirstAddedContent in
+ * mFirstAddedContainer and mLastAddedContent in
+ * mLastAddedContainer and forgets the range.
*/
void MaybeNotifyIMEOfAddedTextDuringDocumentChange();
@@ -232,15 +232,14 @@ private:
*/
bool HasAddedNodesDuringDocumentChange() const
{
- return mFirstAddedNodeContainer && mLastAddedNodeContainer;
+ return mFirstAddedContainer && mLastAddedContainer;
}
/**
- * Returns true if the node at aOffset in aParent is next node of the node at
- * mLastAddedNodeOffset in mLastAddedNodeContainer in pre-order tree
- * traversal of the DOM.
+ * Returns true if the passed-in node in aParent is the next node of
+ * mLastAddedContent in pre-order tree traversal of the DOM.
*/
- bool IsNextNodeOfLastAddedNode(nsINode* aParent, int32_t aOffset) const;
+ bool IsNextNodeOfLastAddedNode(nsINode* aParent, nsIContent* aChild) const;
void PostFocusSetNotification();
void MaybeNotifyIMEOfFocusSet();
@@ -256,7 +255,9 @@ private:
void CancelNotifyingIMEOfPositionChange();
void PostCompositionEventHandledNotification();
- void NotifyContentAdded(nsINode* aContainer, int32_t aStart, int32_t aEnd);
+ void NotifyContentAdded(nsINode* aContainer,
+ nsIContent* aFirstContent,
+ nsIContent* aLastContent);
void ObserveEditableNode();
/**
* NotifyIMEOfBlur() notifies IME of blur.
@@ -431,42 +432,43 @@ private:
*/
struct FlatTextCache
{
- // mContainerNode and mNodeOffset represent a point in DOM tree. E.g.,
- // if mContainerNode is a div element, mNodeOffset is index of its child.
+ // mContainerNode and mNode represent a point in DOM tree. E.g.,
+ // if mContainerNode is a div element, mNode is a child.
nsCOMPtr mContainerNode;
- int32_t mNodeOffset;
+ // mNode points to the last child which participates in the current
+ // mFlatTextLength. If mNode is null, then that means that the end point for
+ // mFlatTextLength is immediately before the first child of mContainerNode.
+ nsCOMPtr mNode;
// Length of flat text generated from contents between the start of content
// and a child node whose index is mNodeOffset of mContainerNode.
uint32_t mFlatTextLength;
FlatTextCache()
- : mNodeOffset(0)
- , mFlatTextLength(0)
+ : mFlatTextLength(0)
{
}
void Clear()
{
mContainerNode = nullptr;
- mNodeOffset = 0;
+ mNode = nullptr;
mFlatTextLength = 0;
}
- void Cache(nsINode* aContainer, int32_t aNodeOffset,
+ void Cache(nsINode* aContainer, nsINode* aNode,
uint32_t aFlatTextLength)
{
MOZ_ASSERT(aContainer, "aContainer must not be null");
- MOZ_ASSERT(
- aNodeOffset <= static_cast(aContainer->GetChildCount()),
- "aNodeOffset must be same as or less than the count of children");
+ MOZ_ASSERT(!aNode || aNode->GetParentNode() == aContainer,
+ "aNode must be either null or a child of aContainer");
mContainerNode = aContainer;
- mNodeOffset = aNodeOffset;
+ mNode = aNode;
mFlatTextLength = aFlatTextLength;
}
- bool Match(nsINode* aContainer, int32_t aNodeOffset) const
+ bool Match(nsINode* aContainer, nsINode* aNode) const
{
- return aContainer == mContainerNode && aNodeOffset == mNodeOffset;
+ return aContainer == mContainerNode && aNode == mNode;
}
};
// mEndOfAddedTextCache caches text length from the start of content to
@@ -479,26 +481,25 @@ private:
// handled by the editor and no other mutation (e.g., adding node) occur.
FlatTextCache mStartOfRemovingTextRangeCache;
- // mFirstAddedNodeContainer is parent node of first added node in current
+ // mFirstAddedContainer is parent node of first added node in current
// document change. So, this is not nullptr only when a node was added
// during a document change and the change has not been included into
// mTextChangeData yet.
// Note that this shouldn't be in cycle collection since this is not nullptr
// only during a document change.
- nsCOMPtr mFirstAddedNodeContainer;
- // mLastAddedNodeContainer is parent node of last added node in current
+ nsCOMPtr mFirstAddedContainer;
+ // mLastAddedContainer is parent node of last added node in current
// document change. So, this is not nullptr only when a node was added
// during a document change and the change has not been included into
// mTextChangeData yet.
// Note that this shouldn't be in cycle collection since this is not nullptr
// only during a document change.
- nsCOMPtr mLastAddedNodeContainer;
- // mFirstAddedNodeOffset is offset of first added node in
- // mFirstAddedNodeContainer.
- int32_t mFirstAddedNodeOffset;
- // mLastAddedNodeOffset is offset of *after* last added node in
- // mLastAddedNodeContainer. I.e., the index of last added node + 1.
- int32_t mLastAddedNodeOffset;
+ nsCOMPtr mLastAddedContainer;
+
+ // mFirstAddedContent is the first node added in mFirstAddedContainer.
+ nsCOMPtr mFirstAddedContent;
+ // mLastAddedContent is the last node added in mLastAddedContainer;
+ nsCOMPtr mLastAddedContent;
TextChangeData mTextChangeData;