Bug 1423331: Prevent selection from leaking from blocks. r=mats

MozReview-Commit-ID: EFLXSQ8Zvze

--HG--
extra : rebase_source : 10b8ff4c0712e6880c69eafc49cf1a78d4dff4bd
This commit is contained in:
Emilio Cobos Álvarez 2017-12-17 02:48:37 +01:00
parent 714ba3c60d
commit d563b8100b

View File

@ -4705,37 +4705,40 @@ struct MOZ_STACK_CLASS FrameContentRange {
// Retrieve the content offsets of a frame
static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
nsCOMPtr<nsIContent> content, parent;
content = aFrame->GetContent();
nsIContent* content = aFrame->GetContent();
if (!content) {
NS_WARNING("Frame has no content");
return FrameContentRange(nullptr, -1, -1);
}
LayoutFrameType type = aFrame->Type();
if (type == LayoutFrameType::Text) {
int32_t offset, offsetEnd;
aFrame->GetOffsets(offset, offsetEnd);
return FrameContentRange(content, offset, offsetEnd);
}
if (type == LayoutFrameType::Br) {
parent = content->GetParent();
nsIContent* parent = content->GetParent();
int32_t beginOffset = parent->IndexOf(content);
return FrameContentRange(parent, beginOffset, beginOffset);
}
// Loop to deal with anonymous content, which has no index; this loop
// probably won't run more than twice under normal conditions
do {
parent = content->GetParent();
if (parent) {
int32_t beginOffset = parent->IndexOf(content);
if (beginOffset >= 0)
return FrameContentRange(parent, beginOffset, beginOffset + 1);
content = parent;
}
} while (parent);
// The root content node must act differently
return FrameContentRange(content, 0, content->GetChildCount());
while (content->IsRootOfAnonymousSubtree()) {
content = content->GetParent();
}
nsIContent* parent = content->GetParent();
if (nsLayoutUtils::GetAsBlock(aFrame) || !parent) {
return FrameContentRange(content, 0, content->GetChildCount());
}
// TODO(emilio): Revise this in presence of Shadow DOM / display: contents,
// it's likely that we don't want to just walk the light tree, and we need to
// change the representation of FrameContentRange.
int32_t index = parent->IndexOf(content);
MOZ_ASSERT(index >= 0);
return FrameContentRange(parent, index, index + 1);
}
// The FrameTarget represents the closest frame to a point that can be selected
@ -4743,20 +4746,22 @@ static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
// frame is the result (in which case different handling is needed), and
// afterFrame says which end is repersented if frameEdge is true
struct FrameTarget {
FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame,
bool aEmptyBlock = false) :
frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame),
emptyBlock(aEmptyBlock) { }
FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame)
: frame(aFrame)
, frameEdge(aFrameEdge)
, afterFrame(aAfterFrame)
{}
static FrameTarget Null() {
return FrameTarget(nullptr, false, false);
}
bool IsNull() {
return !frame;
}
nsIFrame* frame;
bool frameEdge;
bool afterFrame;
bool emptyBlock;
};
// See function implementation for information
@ -4846,11 +4851,12 @@ static FrameTarget GetSelectionClosestFrameForLine(
const nsPoint& aPoint,
uint32_t aFlags)
{
nsIFrame *frame = aLine->mFirstChild;
// Account for end of lines (any iterator from the block is valid)
if (aLine == aParent->LinesEnd())
return DrillDownToSelectionFrame(aParent, true, aFlags);
nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr;
nsIFrame* frame = aLine->mFirstChild;
nsIFrame* closestFromIStart = nullptr;
nsIFrame* closestFromIEnd = nullptr;
nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
WritingMode wm = aLine->mWritingMode;
LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
@ -4909,62 +4915,56 @@ static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
return FrameTarget::Null();
// This code searches for the correct line
nsBlockFrame::LineIterator firstLine = bf->LinesBegin();
nsBlockFrame::LineIterator end = bf->LinesEnd();
if (firstLine == end) {
nsIContent *blockContent = aFrame->GetContent();
if (blockContent) {
// Return with empty flag true.
return FrameTarget(aFrame, false, false, true);
}
return FrameTarget::Null();
}
nsBlockFrame::LineIterator curLine = firstLine;
nsBlockFrame::LineIterator curLine = bf->LinesBegin();
nsBlockFrame::LineIterator closestLine = end;
// Convert aPoint into a LogicalPoint in the writing-mode of this block
WritingMode wm = curLine->mWritingMode;
LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
while (curLine != end) {
// Check to see if our point lies within the line's block-direction bounds
nscoord BCoord = pt.B(wm) - curLine->BStart();
nscoord BSize = curLine->BSize();
if (BCoord >= 0 && BCoord < BSize) {
closestLine = curLine;
break; // We found the line; stop looking
}
if (BCoord < 0)
break;
++curLine;
}
if (closestLine == end) {
nsBlockFrame::LineIterator prevLine = curLine.prev();
nsBlockFrame::LineIterator nextLine = curLine;
// Avoid empty lines
while (nextLine != end && nextLine->IsEmpty())
++nextLine;
while (prevLine != end && prevLine->IsEmpty())
--prevLine;
if (curLine != end) {
// Convert aPoint into a LogicalPoint in the writing-mode of this block
WritingMode wm = curLine->mWritingMode;
LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
do {
// Check to see if our point lies within the line's block-direction bounds
nscoord BCoord = pt.B(wm) - curLine->BStart();
nscoord BSize = curLine->BSize();
if (BCoord >= 0 && BCoord < BSize) {
closestLine = curLine;
break; // We found the line; stop looking
}
if (BCoord < 0)
break;
++curLine;
} while (curLine != end);
// This hidden pref dictates whether a point above or below all lines comes
// up with a line or the beginning or end of the frame; 0 on Windows,
// 1 on other platforms by default at the writing of this code
int32_t dragOutOfFrame =
Preferences::GetInt("browser.drag_out_of_frame_style");
if (closestLine == end) {
nsBlockFrame::LineIterator prevLine = curLine.prev();
nsBlockFrame::LineIterator nextLine = curLine;
// Avoid empty lines
while (nextLine != end && nextLine->IsEmpty())
++nextLine;
while (prevLine != end && prevLine->IsEmpty())
--prevLine;
if (prevLine == end) {
if (dragOutOfFrame == 1 || nextLine == end)
return DrillDownToSelectionFrame(aFrame, false, aFlags);
closestLine = nextLine;
} else if (nextLine == end) {
if (dragOutOfFrame == 1)
return DrillDownToSelectionFrame(aFrame, true, aFlags);
closestLine = prevLine;
} else { // Figure out which line is closer
if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
closestLine = prevLine;
else
// This hidden pref dictates whether a point above or below all lines comes
// up with a line or the beginning or end of the frame; 0 on Windows,
// 1 on other platforms by default at the writing of this code
int32_t dragOutOfFrame =
Preferences::GetInt("browser.drag_out_of_frame_style");
if (prevLine == end) {
if (dragOutOfFrame == 1 || nextLine == end)
return DrillDownToSelectionFrame(aFrame, false, aFlags);
closestLine = nextLine;
} else if (nextLine == end) {
if (dragOutOfFrame == 1)
return DrillDownToSelectionFrame(aFrame, true, aFlags);
closestLine = prevLine;
} else { // Figure out which line is closer
if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
closestLine = prevLine;
else
closestLine = nextLine;
}
}
}
@ -4975,6 +4975,7 @@ static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
return target;
++closestLine;
} while (closestLine != end);
// Fall back to just targeting the last targetable place
return DrillDownToSelectionFrame(aFrame, true, aFlags);
}
@ -5112,17 +5113,6 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(const nsPoint& aPo
FrameTarget closest =
GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
if (closest.emptyBlock) {
ContentOffsets offsets;
NS_ASSERTION(closest.frame,
"closest.frame must not be null when it's empty");
offsets.content = closest.frame->GetContent();
offsets.offset = 0;
offsets.secondaryOffset = 0;
offsets.associate = CARET_ASSOCIATE_AFTER;
return offsets;
}
// If the correct offset is at one end of a frame, use offset-based
// calculation method
if (closest.frameEdge) {
@ -8345,7 +8335,7 @@ nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos)
nsIFrame* frame = this;
nsContentAndOffset blockFrameOrBR;
blockFrameOrBR.mContent = nullptr;
bool reachedBlockAncestor = false;
bool reachedBlockAncestor = !!nsLayoutUtils::GetAsBlock(frame);
// Go through containing frames until reaching a block frame.
// In each step, search the previous (or next) siblings for the closest
@ -8396,7 +8386,7 @@ nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct *aPos)
break;
}
frame = parent;
reachedBlockAncestor = (nsLayoutUtils::GetAsBlock(frame) != nullptr);
reachedBlockAncestor = !!nsLayoutUtils::GetAsBlock(frame);
}
if (reachedBlockAncestor) { // no "stop frame" found
aPos->mResultContent = frame->GetContent();