Bug 1646296 - part 8: Make stack only classes to group start/end boundary information of WSRunScanner r=m_kato

Differential Revision: https://phabricator.services.mozilla.com/D79973
This commit is contained in:
Masayuki Nakano 2020-06-22 14:48:15 +00:00
parent 5b98dad2b2
commit 6b7588bbe2
2 changed files with 220 additions and 192 deletions

View File

@ -74,15 +74,9 @@ WSRunScanner::WSRunScanner(const HTMLEditor* aHTMLEditor,
mScanEndPoint(aScanEndPoint),
mEditingHost(aHTMLEditor->GetActiveEditingHost()),
mPRE(false),
mStartOffset(0),
mEndOffset(0),
mFirstNBSPOffset(0),
mLastNBSPOffset(0),
mStartRun(nullptr),
mEndRun(nullptr),
mHTMLEditor(aHTMLEditor),
mStartReason(WSType::NotInitialized),
mEndReason(WSType::NotInitialized) {
mHTMLEditor(aHTMLEditor) {
MOZ_ASSERT(
*nsContentUtils::ComparePoints(aScanStartPoint.ToRawRangeBoundary(),
aScanEndPoint.ToRawRangeBoundary()) <= 0);
@ -424,8 +418,8 @@ nsresult WSRunObject::InsertText(Document& aDocument,
} else if (afterRunObject.EndsByBlockBoundary()) {
// When afterRun is null, it means that mScanEndPoint is last point in
// editing host or editing block.
// If this text insertion replaces composition, this.mEndReason is
// start position of compositon. So we have to use afterRunObject's
// If this text insertion replaces composition, this.mEnd.mReason is
// start position of composition. So we have to use afterRunObject's
// reason instead.
theString.SetCharAt(kNBSP, lastCharIndex);
}
@ -635,12 +629,11 @@ WSScanResult WSRunScanner::ScanPreviousVisibleNodeOrBlockBoundaryFrom(
}
}
if (mStartReasonContent != mStartNode) {
// In this case, mStartOffset is not meaningful.
return WSScanResult(mStartReasonContent, mStartReason);
if (mStart.GetReasonContent() != mStart.PointRef().GetContainer()) {
// In this case, mStart.PointRef().Offset() is not meaningful.
return WSScanResult(mStart.GetReasonContent(), mStart.RawReason());
}
return WSScanResult(EditorDOMPoint(mStartReasonContent, mStartOffset),
mStartReason);
return WSScanResult(mStart.PointRef(), mStart.RawReason());
}
template <typename PT, typename CT>
@ -671,19 +664,18 @@ WSScanResult WSRunScanner::ScanNextVisibleNodeOrBlockBoundaryFrom(
}
}
if (mEndReasonContent != mEndNode) {
// In this case, mEndOffset is not meaningful.
return WSScanResult(mEndReasonContent, mEndReason);
if (mEnd.GetReasonContent() != mEnd.PointRef().GetContainer()) {
// In this case, mEnd.PointRef().Offset() is not meaningful.
return WSScanResult(mEnd.GetReasonContent(), mEnd.RawReason());
}
return WSScanResult(EditorDOMPoint(mEndReasonContent, mEndOffset),
mEndReason);
return WSScanResult(mEnd.PointRef(), mEnd.RawReason());
}
nsresult WSRunObject::AdjustWhiteSpace() {
// this routine examines a run of ws and tries to get rid of some unneeded
// nbsp's, replacing them with regualr ascii space if possible. Keeping
// nbsp's, replacing them with regular ascii space if possible. Keeping
// things simple for now and just trying to fix up the trailing ws in the run.
if (!mLastNBSPNode) {
if (!mNBSPData.FoundNBSP()) {
// nothing to do!
return NS_OK;
}
@ -770,19 +762,14 @@ bool WSRunScanner::InitializeRangeStartWithTextNode(
}
if (ch == HTMLEditUtils::kNBSP) {
mFirstNBSPNode = aPoint.ContainerAsText();
mFirstNBSPOffset = i - 1;
if (!mLastNBSPNode) {
mLastNBSPNode = aPoint.ContainerAsText();
mLastNBSPOffset = i - 1;
}
mNBSPData.NotifyNBSP(
EditorDOMPointInText(aPoint.ContainerAsText(), i - 1),
NoBreakingSpaceData::Scanning::Backward);
continue;
}
mStartNode = aPoint.ContainerAsText();
mStartOffset = i;
mStartReason = WSType::NormalText;
mStartReasonContent = aPoint.ContainerAsText();
mStart = BoundaryData(EditorDOMPoint(aPoint.ContainerAsText(), i),
*aPoint.ContainerAsText(), WSType::NormalText);
return true;
}
@ -815,21 +802,19 @@ void WSRunScanner::InitializeRangeStart(
if (!previousLeafContentOrBlock) {
// no prior node means we exhausted
// aEditableBlockParentOrTopmostEditableInlineContent
mStartNode = aPoint.GetContainer();
mStartOffset = aPoint.Offset();
mStartReason = WSType::CurrentBlockBoundary;
// mStartReasonContent can be either a block element or any non-editable
// mStart.mReasonContent can be either a block element or any non-editable
// content in this case.
mStartReasonContent = const_cast<nsIContent*>(
&aEditableBlockParentOrTopmostEditableInlineContent);
mStart =
BoundaryData(aPoint,
const_cast<nsIContent&>(
aEditableBlockParentOrTopmostEditableInlineContent),
WSType::CurrentBlockBoundary);
return;
}
if (HTMLEditUtils::IsBlockElement(*previousLeafContentOrBlock)) {
mStartNode = aPoint.GetContainer();
mStartOffset = aPoint.Offset();
mStartReason = WSType::OtherBlockBoundary;
mStartReasonContent = previousLeafContentOrBlock;
mStart = BoundaryData(aPoint, *previousLeafContentOrBlock,
WSType::OtherBlockBoundary);
return;
}
@ -837,12 +822,11 @@ void WSRunScanner::InitializeRangeStart(
!previousLeafContentOrBlock->IsEditable()) {
// it's a break or a special node, like <img>, that is not a block and
// not a break but still serves as a terminator to ws runs.
mStartNode = aPoint.GetContainer();
mStartOffset = aPoint.Offset();
mStartReason = previousLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
? WSType::BRElement
: WSType::SpecialContent;
mStartReasonContent = previousLeafContentOrBlock;
mStart =
BoundaryData(aPoint, *previousLeafContentOrBlock,
previousLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
? WSType::BRElement
: WSType::SpecialContent);
return;
}
@ -881,19 +865,13 @@ bool WSRunScanner::InitializeRangeEndWithTextNode(
}
if (ch == HTMLEditUtils::kNBSP) {
mLastNBSPNode = aPoint.ContainerAsText();
mLastNBSPOffset = i;
if (!mFirstNBSPNode) {
mFirstNBSPNode = aPoint.ContainerAsText();
mFirstNBSPOffset = i;
}
mNBSPData.NotifyNBSP(EditorDOMPointInText(aPoint.ContainerAsText(), i),
NoBreakingSpaceData::Scanning::Forward);
continue;
}
mEndNode = aPoint.ContainerAsText();
mEndOffset = i;
mEndReason = WSType::NormalText;
mEndReasonContent = aPoint.ContainerAsText();
mEnd = BoundaryData(EditorDOMPoint(aPoint.ContainerAsText(), i),
*aPoint.ContainerAsText(), WSType::NormalText);
return true;
}
@ -925,22 +903,19 @@ void WSRunScanner::InitializeRangeEnd(
if (!nextLeafContentOrBlock) {
// no next node means we exhausted
// aEditableBlockParentOrTopmostEditableInlineContent
mEndNode = aPoint.GetContainer();
mEndOffset = aPoint.Offset();
mEndReason = WSType::CurrentBlockBoundary;
// mEndReasonContent can be either a block element or any non-editable
// mEnd.mReasonContent can be either a block element or any non-editable
// content in this case.
mEndReasonContent = const_cast<nsIContent*>(
&aEditableBlockParentOrTopmostEditableInlineContent);
mEnd = BoundaryData(aPoint,
const_cast<nsIContent&>(
aEditableBlockParentOrTopmostEditableInlineContent),
WSType::CurrentBlockBoundary);
return;
}
if (HTMLEditUtils::IsBlockElement(*nextLeafContentOrBlock)) {
// we encountered a new block. therefore no more ws.
mEndNode = aPoint.GetContainer();
mEndOffset = aPoint.Offset();
mEndReason = WSType::OtherBlockBoundary;
mEndReasonContent = nextLeafContentOrBlock;
mEnd = BoundaryData(aPoint, *nextLeafContentOrBlock,
WSType::OtherBlockBoundary);
return;
}
@ -949,12 +924,10 @@ void WSRunScanner::InitializeRangeEnd(
// we encountered a break or a special node, like <img>,
// that is not a block and not a break but still
// serves as a terminator to ws runs.
mEndNode = aPoint.GetContainer();
mEndOffset = aPoint.Offset();
mEndReason = nextLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
? WSType::BRElement
: WSType::SpecialContent;
mEndReasonContent = nextLeafContentOrBlock;
mEnd = BoundaryData(aPoint, *nextLeafContentOrBlock,
nextLeafContentOrBlock->IsHTMLElement(nsGkAtoms::br)
? WSType::BRElement
: WSType::SpecialContent);
return;
}
@ -1002,7 +975,7 @@ void WSRunScanner::GetRuns() {
// if we are before or after a block (or after a break), and there are no
// nbsp's, then it's all non-rendering ws.
if (!mFirstNBSPNode && !mLastNBSPNode &&
if (!mNBSPData.FoundNBSP() &&
(StartsFromHardLineBreak() || EndsByBlockBoundary())) {
InitializeWithSingleFragment(
WSFragment::Visible::No,
@ -1015,57 +988,74 @@ void WSRunScanner::GetRuns() {
// otherwise a little trickier. shucks.
mStartRun = new WSFragment();
mStartRun->mStartNode = mStartNode;
mStartRun->mStartOffset = mStartOffset;
if (mStart.PointRef().IsSet()) {
mStartRun->mStartNode = mStart.PointRef().GetContainer();
mStartRun->mStartOffset = mStart.PointRef().Offset();
}
if (StartsFromHardLineBreak()) {
// set up mStartRun
mStartRun->MarkAsStartOfHardLine();
mStartRun->mEndNode = mFirstNBSPNode;
mStartRun->mEndOffset = mFirstNBSPOffset;
mStartRun->SetStartFrom(mStartReason);
if (mNBSPData.FirstPointRef().IsSet()) {
mStartRun->mEndNode = mNBSPData.FirstPointRef().GetContainer();
mStartRun->mEndOffset = mNBSPData.FirstPointRef().Offset();
}
mStartRun->SetStartFrom(mStart.RawReason());
mStartRun->SetEndByNormalWiteSpaces();
// set up next run
WSFragment* normalRun = new WSFragment();
mStartRun->mRight = normalRun;
normalRun->MarkAsVisible();
normalRun->mStartNode = mFirstNBSPNode;
normalRun->mStartOffset = mFirstNBSPOffset;
if (mNBSPData.FirstPointRef().IsSet()) {
normalRun->mStartNode = mNBSPData.FirstPointRef().GetContainer();
normalRun->mStartOffset = mNBSPData.FirstPointRef().Offset();
}
normalRun->SetStartFromLeadingWhiteSpaces();
normalRun->mLeft = mStartRun;
if (!EndsByBlockBoundary()) {
// then no trailing ws. this normal run ends the overall ws run.
normalRun->SetEndBy(mEndReason);
normalRun->mEndNode = mEndNode;
normalRun->mEndOffset = mEndOffset;
normalRun->SetEndBy(mEnd.RawReason());
if (mEnd.PointRef().IsSet()) {
normalRun->mEndNode = mEnd.PointRef().GetContainer();
normalRun->mEndOffset = mEnd.PointRef().Offset();
}
mEndRun = normalRun;
} else {
// we might have trailing ws.
// it so happens that *if* there is an nbsp at end,
// {mEndNode,mEndOffset-1} will point to it, even though in general
// start/end points not guaranteed to be in text nodes.
if (mLastNBSPNode == mEndNode && mLastNBSPOffset == mEndOffset - 1) {
if (mNBSPData.LastPointRef().IsSet() && mEnd.PointRef().IsSet() &&
mNBSPData.LastPointRef().GetContainer() ==
mEnd.PointRef().GetContainer() &&
mNBSPData.LastPointRef().Offset() == mEnd.PointRef().Offset() - 1) {
// normal ws runs right up to adjacent block (nbsp next to block)
normalRun->SetEndBy(mEndReason);
normalRun->mEndNode = mEndNode;
normalRun->mEndOffset = mEndOffset;
normalRun->SetEndBy(mEnd.RawReason());
normalRun->mEndNode = mEnd.PointRef().GetContainer();
normalRun->mEndOffset = mEnd.PointRef().Offset();
mEndRun = normalRun;
} else {
normalRun->mEndNode = mLastNBSPNode;
normalRun->mEndOffset = mLastNBSPOffset + 1;
if (mNBSPData.LastPointRef().IsSet()) {
normalRun->mEndNode = mNBSPData.LastPointRef().GetContainer();
normalRun->mEndOffset = mNBSPData.LastPointRef().Offset() + 1;
}
normalRun->SetEndByTrailingWhiteSpaces();
// set up next run
WSFragment* lastRun = new WSFragment();
lastRun->MarkAsEndOfHardLine();
lastRun->mStartNode = mLastNBSPNode;
lastRun->mStartOffset = mLastNBSPOffset + 1;
lastRun->mEndNode = mEndNode;
lastRun->mEndOffset = mEndOffset;
if (mNBSPData.LastPointRef().IsSet()) {
lastRun->mStartNode = mNBSPData.LastPointRef().GetContainer();
lastRun->mStartOffset = mNBSPData.LastPointRef().Offset() + 1;
}
if (mEnd.PointRef().IsSet()) {
lastRun->mEndNode = mEnd.PointRef().GetContainer();
lastRun->mEndOffset = mEnd.PointRef().Offset();
}
lastRun->SetStartFromNormalWhiteSpaces();
lastRun->mLeft = normalRun;
lastRun->SetEndBy(mEndReason);
lastRun->SetEndBy(mEnd.RawReason());
mEndRun = lastRun;
normalRun->mRight = lastRun;
}
@ -1073,28 +1063,35 @@ void WSRunScanner::GetRuns() {
} else {
MOZ_ASSERT(!StartsFromHardLineBreak());
mStartRun->MarkAsVisible();
mStartRun->mEndNode = mLastNBSPNode;
mStartRun->mEndOffset = mLastNBSPOffset + 1;
mStartRun->SetStartFrom(mStartReason);
if (mNBSPData.LastPointRef().IsSet()) {
mStartRun->mEndNode = mNBSPData.LastPointRef().GetContainer();
mStartRun->mEndOffset = mNBSPData.LastPointRef().Offset() + 1;
}
mStartRun->SetStartFrom(mStart.RawReason());
// we might have trailing ws.
// it so happens that *if* there is an nbsp at end, {mEndNode,mEndOffset-1}
// will point to it, even though in general start/end points not
// guaranteed to be in text nodes.
if (mLastNBSPNode == mEndNode && mLastNBSPOffset == (mEndOffset - 1)) {
mStartRun->SetEndBy(mEndReason);
mStartRun->mEndNode = mEndNode;
mStartRun->mEndOffset = mEndOffset;
if (mNBSPData.LastPointRef().IsSet() && mEnd.PointRef().IsSet() &&
mNBSPData.LastPointRef().GetContainer() ==
mEnd.PointRef().GetContainer() &&
mNBSPData.LastPointRef().Offset() == mEnd.PointRef().Offset() - 1) {
mStartRun->SetEndBy(mEnd.RawReason());
mStartRun->mEndNode = mEnd.PointRef().GetContainer();
mStartRun->mEndOffset = mEnd.PointRef().Offset();
mEndRun = mStartRun;
} else {
// set up next run
WSFragment* lastRun = new WSFragment();
lastRun->MarkAsEndOfHardLine();
lastRun->mStartNode = mLastNBSPNode;
lastRun->mStartOffset = mLastNBSPOffset + 1;
if (mNBSPData.LastPointRef().IsSet()) {
lastRun->mStartNode = mNBSPData.LastPointRef().GetContainer();
lastRun->mStartOffset = mNBSPData.LastPointRef().Offset() + 1;
}
lastRun->SetStartFromNormalWhiteSpaces();
lastRun->mLeft = mStartRun;
lastRun->SetEndBy(mEndReason);
lastRun->SetEndBy(mEnd.RawReason());
mEndRun = lastRun;
mStartRun->mRight = lastRun;
mStartRun->SetEndByTrailingWhiteSpaces();
@ -1123,8 +1120,10 @@ void WSRunScanner::InitializeWithSingleFragment(
mStartRun = new WSFragment();
mStartRun->mStartNode = mStartNode;
mStartRun->mStartOffset = mStartOffset;
if (mStart.PointRef().IsSet()) {
mStartRun->mStartNode = mStart.PointRef().GetContainer();
mStartRun->mStartOffset = mStart.PointRef().Offset();
}
if (aIsVisible == WSFragment::Visible::Yes) {
mStartRun->MarkAsVisible();
}
@ -1134,10 +1133,12 @@ void WSRunScanner::InitializeWithSingleFragment(
if (aIsEndOfHardLine == WSFragment::EndOfHardLine::Yes) {
mStartRun->MarkAsEndOfHardLine();
}
mStartRun->mEndNode = mEndNode;
mStartRun->mEndOffset = mEndOffset;
mStartRun->SetStartFrom(mStartReason);
mStartRun->SetEndBy(mEndReason);
if (mEnd.PointRef().IsSet()) {
mStartRun->mEndNode = mEnd.PointRef().GetContainer();
mStartRun->mEndOffset = mEnd.PointRef().Offset();
}
mStartRun->SetStartFrom(mStart.RawReason());
mStartRun->SetEndBy(mEnd.RawReason());
mEndRun = mStartRun;
}
@ -1353,7 +1354,7 @@ EditorDOMPointInText WSRunScanner::GetInclusiveNextEditableCharPoint(
return EditorDOMPointInText(point.ContainerAsText(), point.Offset());
}
if (point.GetContainer() == mEndReasonContent) {
if (point.GetContainer() == mEnd.GetReasonContent()) {
return EditorDOMPointInText();
}
@ -1375,7 +1376,7 @@ EditorDOMPointInText WSRunScanner::GetInclusiveNextEditableCharPoint(
*nextContent, *editableBlockParentOrTopmotEditableInlineContent,
mEditingHost)) {
if (!nextContent->IsText() || !nextContent->IsEditable()) {
if (nextContent == mEndReasonContent) {
if (nextContent == mEnd.GetReasonContent()) {
break; // Reached end of current runs.
}
continue;
@ -1421,7 +1422,7 @@ EditorDOMPointInText WSRunScanner::GetPreviousEditableCharPoint(
return EditorDOMPointInText(point.ContainerAsText(), point.Offset() - 1);
}
if (point.GetContainer() == mStartReasonContent) {
if (point.GetContainer() == mStart.GetReasonContent()) {
return EditorDOMPointInText();
}
@ -1445,7 +1446,7 @@ EditorDOMPointInText WSRunScanner::GetPreviousEditableCharPoint(
*editableBlockParentOrTopmotEditableInlineContent,
mEditingHost)) {
if (!previousContent->IsText() || !previousContent->IsEditable()) {
if (previousContent == mStartReasonContent) {
if (previousContent == mStart.GetReasonContent()) {
break; // Reached start of current runs.
}
continue;

View File

@ -379,62 +379,42 @@ class MOZ_STACK_CLASS WSRunScanner {
* which is editable, but also may be non-element and/or non-editable. See
* MOZ_ASSERT_IF()s in WSScanResult::AssertIfInvalidData() for the detail.
*/
nsIContent* GetStartReasonContent() const { return mStartReasonContent; }
nsIContent* GetEndReasonContent() const { return mEndReasonContent; }
nsIContent* GetStartReasonContent() const {
return mStart.GetReasonContent();
}
nsIContent* GetEndReasonContent() const { return mEnd.GetReasonContent(); }
bool StartsFromNormalText() const {
return mStartReason == WSType::NormalText;
}
bool StartsFromSpecialContent() const {
return mStartReason == WSType::SpecialContent;
}
bool StartsFromBRElement() const { return mStartReason == WSType::BRElement; }
bool StartsFromNormalText() const { return mStart.IsNormalText(); }
bool StartsFromSpecialContent() const { return mStart.IsSpecialContent(); }
bool StartsFromBRElement() const { return mStart.IsBRElement(); }
bool StartsFromCurrentBlockBoundary() const {
return mStartReason == WSType::CurrentBlockBoundary;
return mStart.IsCurrentBlockBoundary();
}
bool StartsFromOtherBlockElement() const {
return mStartReason == WSType::OtherBlockBoundary;
return mStart.IsOtherBlockBoundary();
}
bool StartsFromBlockBoundary() const {
return mStartReason == WSType::CurrentBlockBoundary ||
mStartReason == WSType::OtherBlockBoundary;
}
bool StartsFromHardLineBreak() const {
return mStartReason == WSType::CurrentBlockBoundary ||
mStartReason == WSType::OtherBlockBoundary ||
mStartReason == WSType::BRElement;
}
bool EndsByNormalText() const { return mEndReason == WSType::NormalText; }
bool EndsBySpecialContent() const {
return mEndReason == WSType::SpecialContent;
}
bool EndsByBRElement() const { return mEndReason == WSType::BRElement; }
bool StartsFromBlockBoundary() const { return mStart.IsBlockBoundary(); }
bool StartsFromHardLineBreak() const { return mStart.IsHardLineBreak(); }
bool EndsByNormalText() const { return mEnd.IsNormalText(); }
bool EndsBySpecialContent() const { return mEnd.IsSpecialContent(); }
bool EndsByBRElement() const { return mEnd.IsBRElement(); }
bool EndsByCurrentBlockBoundary() const {
return mEndReason == WSType::CurrentBlockBoundary;
}
bool EndsByOtherBlockElement() const {
return mEndReason == WSType::OtherBlockBoundary;
}
bool EndsByBlockBoundary() const {
return mEndReason == WSType::CurrentBlockBoundary ||
mEndReason == WSType::OtherBlockBoundary;
return mEnd.IsCurrentBlockBoundary();
}
bool EndsByOtherBlockElement() const { return mEnd.IsOtherBlockBoundary(); }
bool EndsByBlockBoundary() const { return mEnd.IsBlockBoundary(); }
MOZ_NEVER_INLINE_DEBUG dom::Element* StartReasonOtherBlockElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mStartReasonContent->IsElement());
return mStartReasonContent->AsElement();
return mStart.OtherBlockElementPtr();
}
MOZ_NEVER_INLINE_DEBUG dom::HTMLBRElement* StartReasonBRElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mStartReasonContent->IsHTMLElement(nsGkAtoms::br));
return static_cast<dom::HTMLBRElement*>(mStartReasonContent.get());
return mStart.BRElementPtr();
}
MOZ_NEVER_INLINE_DEBUG dom::Element* EndReasonOtherBlockElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mEndReasonContent->IsElement());
return mEndReasonContent->AsElement();
return mEnd.OtherBlockElementPtr();
}
MOZ_NEVER_INLINE_DEBUG dom::HTMLBRElement* EndReasonBRElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mEndReasonContent->IsHTMLElement(nsGkAtoms::br));
return static_cast<dom::HTMLBRElement*>(mEndReasonContent.get());
return mEnd.BRElementPtr();
}
/**
@ -580,7 +560,7 @@ class MOZ_STACK_CLASS WSRunScanner {
* GetInclusiveNextEditableCharPoint() returns aPoint if it points a character
* in an editable text node, or start of next editable text node otherwise.
* FYI: For the performance, this does not check whether given container
* is not after mStartReasonContent or not.
* is not after mStart.mReasonContent or not.
*/
template <typename PT, typename CT>
EditorDOMPointInText GetInclusiveNextEditableCharPoint(
@ -591,7 +571,7 @@ class MOZ_STACK_CLASS WSRunScanner {
* text node. Note that this returns last character point when it meets
* non-empty text node, otherwise, returns a point in an empty text node.
* FYI: For the performance, this does not check whether given container
* is not before mEndReasonContent or not.
* is not before mEnd.mReasonContent or not.
*/
template <typename PT, typename CT>
EditorDOMPointInText GetPreviousEditableCharPoint(
@ -632,6 +612,83 @@ class MOZ_STACK_CLASS WSRunScanner {
char16_t GetCharAt(dom::Text* aTextNode, int32_t aOffset) const;
class MOZ_STACK_CLASS BoundaryData final {
public:
BoundaryData() : mReason(WSType::NotInitialized) {}
template <typename EditorDOMPointType>
BoundaryData(const EditorDOMPointType& aPoint, nsIContent& aReasonContent,
WSType aReason)
: mReasonContent(&aReasonContent), mPoint(aPoint), mReason(aReason) {}
bool Initialized() const { return mReasonContent && mPoint.IsSet(); }
nsIContent* GetReasonContent() const { return mReasonContent; }
const EditorDOMPoint& PointRef() const { return mPoint; }
WSType RawReason() const { return mReason; }
bool IsNormalText() const { return mReason == WSType::NormalText; }
bool IsSpecialContent() const { return mReason == WSType::SpecialContent; }
bool IsBRElement() const { return mReason == WSType::BRElement; }
bool IsCurrentBlockBoundary() const {
return mReason == WSType::CurrentBlockBoundary;
}
bool IsOtherBlockBoundary() const {
return mReason == WSType::OtherBlockBoundary;
}
bool IsBlockBoundary() const {
return mReason == WSType::CurrentBlockBoundary ||
mReason == WSType::OtherBlockBoundary;
}
bool IsHardLineBreak() const {
return mReason == WSType::CurrentBlockBoundary ||
mReason == WSType::OtherBlockBoundary ||
mReason == WSType::BRElement;
}
MOZ_NEVER_INLINE_DEBUG dom::Element* OtherBlockElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mReasonContent->IsElement());
return mReasonContent->AsElement();
}
MOZ_NEVER_INLINE_DEBUG dom::HTMLBRElement* BRElementPtr() const {
MOZ_DIAGNOSTIC_ASSERT(mReasonContent->IsHTMLElement(nsGkAtoms::br));
return static_cast<dom::HTMLBRElement*>(mReasonContent.get());
}
private:
nsCOMPtr<nsIContent> mReasonContent;
EditorDOMPoint mPoint;
// Must be one of WSType::NotInitialized, WSType::NormalText,
// WSType::SpecialContent, WSType::BRElement, WSType::CurrentBlockBoundary
// or WSType::OtherBlockBoundary.
WSType mReason;
};
class MOZ_STACK_CLASS NoBreakingSpaceData final {
public:
enum class Scanning { Forward, Backward };
void NotifyNBSP(const EditorDOMPointInText& aPoint,
Scanning aScanningDirection) {
MOZ_ASSERT(aPoint.IsSetAndValid());
MOZ_ASSERT(aPoint.IsCharNBSP());
if (!mFirst.IsSet() || aScanningDirection == Scanning::Backward) {
mFirst = aPoint;
}
if (!mLast.IsSet() || aScanningDirection == Scanning::Forward) {
mLast = aPoint;
}
}
const EditorDOMPointInText& FirstPointRef() const { return mFirst; }
const EditorDOMPointInText& LastPointRef() const { return mLast; }
bool FoundNBSP() const {
MOZ_ASSERT(mFirst.IsSet() == mLast.IsSet());
return mFirst.IsSet();
}
private:
EditorDOMPointInText mFirst;
EditorDOMPointInText mLast;
};
void GetRuns();
void ClearRuns();
void InitializeWithSingleFragment(
@ -663,42 +720,19 @@ class MOZ_STACK_CLASS WSRunScanner {
// true if we are in preformatted white-space context.
bool mPRE;
// Node/offset where ws starts and ends.
nsCOMPtr<nsINode> mStartNode;
int32_t mStartOffset;
// Node/offset where ws ends.
nsCOMPtr<nsINode> mEndNode;
int32_t mEndOffset;
// Location of first nbsp in ws run, if any.
RefPtr<dom::Text> mFirstNBSPNode;
int32_t mFirstNBSPOffset;
// Location of last nbsp in ws run, if any.
RefPtr<dom::Text> mLastNBSPNode;
int32_t mLastNBSPOffset;
// The first WSFragment in the run.
WSFragment* mStartRun;
// The last WSFragment in the run, may be same as first.
WSFragment* mEndRun;
// See above comment for GetStartReasonContent() and GetEndReasonContent().
nsCOMPtr<nsIContent> mStartReasonContent;
nsCOMPtr<nsIContent> mEndReasonContent;
// Non-owning.
const HTMLEditor* mHTMLEditor;
private:
// Must be one of WSType::NotInitialized, WSType::NormalText,
// WSType::SpecialContent, WSType::BRElement, WSType::CurrentBlockBoundary or
// WSType::OtherBlockBoundary. Access these values with StartsFrom*() and
// EndsBy*() accessors.
WSType mStartReason;
WSType mEndReason;
protected:
BoundaryData mStart;
BoundaryData mEnd;
NoBreakingSpaceData mNBSPData;
};
class MOZ_STACK_CLASS WSRunObject final : public WSRunScanner {
@ -896,13 +930,6 @@ class MOZ_STACK_CLASS WSRunObject final : public WSRunScanner {
MOZ_CAN_RUN_SCRIPT nsresult Scrub();
EditorDOMPoint StartPoint() const {
return EditorDOMPoint(mStartNode, mStartOffset);
}
EditorDOMPoint EndPoint() const {
return EditorDOMPoint(mEndNode, mEndOffset);
}
// Because of MOZ_CAN_RUN_SCRIPT constructors, each instanciater of this class
// guarantees the lifetime of the HTMLEditor.
HTMLEditor& mHTMLEditor;