--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -8,16 +8,17 @@
#include "gfxPrefs.h"
#include "nsBidiPresUtils.h"
#include "nsCanvasFrame.h"
#include "nsCaret.h"
#include "nsContentUtils.h"
#include "nsDebug.h"
#include "nsDOMTokenList.h"
+#include "nsFocusManager.h"
#include "nsFrame.h"
#include "nsIDocument.h"
#include "nsIDocShell.h"
#include "nsIDOMDocument.h"
#include "nsIDOMNodeFilter.h"
#include "nsIPresShell.h"
#include "nsPresContext.h"
#include "nsRect.h"
@@ -325,17 +326,16 @@ FindFirstNodeWithFrame(nsIDocument* aDoc
nsCOMPtr<nsINode> startNode =
do_QueryInterface(aBackward ? aRange->GetEndParent() : aRange->GetStartParent());
nsCOMPtr<nsINode> endNode =
do_QueryInterface(aBackward ? aRange->GetStartParent() : aRange->GetEndParent());
int32_t offset = aBackward ? aRange->EndOffset() : aRange->StartOffset();
nsCOMPtr<nsIContent> startContent = do_QueryInterface(startNode);
- nsCOMPtr<nsIContent> endContent = do_QueryInterface(endNode);
CaretAssociationHint hintStart =
aBackward ? CARET_ASSOCIATE_BEFORE : CARET_ASSOCIATE_AFTER;
nsIFrame* startFrame = aFrameSelection->GetFrameForNodeOffset(startContent,
offset,
hintStart,
&aOutOffset);
if (startFrame) {
@@ -355,36 +355,40 @@ FindFirstNodeWithFrame(nsIDocument* aDoc
startFrame = startContent ? startContent->GetPrimaryFrame() : nullptr;
while (!startFrame && startNode != endNode) {
if (aBackward) {
startNode = walker->PreviousNode(err);
} else {
startNode = walker->NextNode(err);
}
+
+ if (!startNode) {
+ break;
+ }
+
startContent = do_QueryInterface(startNode);
startFrame = startContent ? startContent->GetPrimaryFrame() : nullptr;
}
return startFrame;
}
void
SelectionCarets::UpdateSelectionCarets()
{
if (!mPresShell) {
return;
}
- nsISelection* caretSelection = GetSelection();
- if (!caretSelection) {
+ nsRefPtr<dom::Selection> selection = GetSelection();
+ if (!selection) {
SetVisibility(false);
return;
}
- nsRefPtr<dom::Selection> selection = static_cast<dom::Selection*>(caretSelection);
if (selection->GetRangeCount() <= 0) {
SetVisibility(false);
return;
}
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
if (range->Collapsed()) {
SetVisibility(false);
@@ -399,53 +403,39 @@ SelectionCarets::UpdateSelectionCarets()
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
nsIFrame* rootFrame = mPresShell->GetRootFrame();
if (!canvasFrame || !rootFrame) {
SetVisibility(false);
return;
}
- // Check if caret inside the scroll frame's boundary
- nsIFrame* caretFocusFrame = GetCaretFocusFrame();
- if (!caretFocusFrame) {
- SetVisibility(false);
- return;
- }
- nsIContent *editableAncestor = caretFocusFrame->GetContent()->GetEditingHost();
-
- if (!editableAncestor) {
- SetVisibility(false);
- return;
- }
-
- nsRect resultRect;
- for (nsIFrame* frame = editableAncestor->GetPrimaryFrame();
- frame != nullptr;
- frame = frame->GetNextContinuation()) {
- nsRect rect = frame->GetRectRelativeToSelf();
- nsLayoutUtils::TransformRect(frame, rootFrame, rect);
- resultRect = resultRect.Union(rect);
- }
-
// Check start and end frame is rtl or ltr text
- nsRefPtr<nsFrameSelection> fs = caretFocusFrame->GetFrameSelection();
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
int32_t startOffset;
nsIFrame* startFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
range, fs, false, startOffset);
int32_t endOffset;
nsIFrame* endFrame = FindFirstNodeWithFrame(mPresShell->GetDocument(),
range, fs, true, endOffset);
if (!startFrame || !endFrame) {
SetVisibility(false);
return;
}
+ // If frame isn't editable and we don't support non-editable fields, bail
+ // out.
+ if (!kSupportNonEditableFields &&
+ (!startFrame->GetContent()->IsEditable() ||
+ !endFrame->GetContent()->IsEditable())) {
+ return;
+ }
+
// Check if startFrame is after endFrame.
if (nsLayoutUtils::CompareTreePosition(startFrame, endFrame) > 0) {
SetVisibility(false);
return;
}
bool startFrameIsRTL = IsRightToLeft(startFrame);
bool endFrameIsRTL = IsRightToLeft(endFrame);
@@ -453,22 +443,38 @@ SelectionCarets::UpdateSelectionCarets()
// If start frame is LTR, then place start caret in first rect's leftmost
// otherwise put it to first rect's rightmost.
ReduceRectToVerticalEdge(collector.mFirstRect, startFrameIsRTL);
// Contrary to start frame, if end frame is LTR, put end caret to last
// rect's rightmost position, otherwise, put it to last rect's leftmost.
ReduceRectToVerticalEdge(collector.mLastRect, !endFrameIsRTL);
- SetStartFrameVisibility(resultRect.Intersects(collector.mFirstRect));
- SetEndFrameVisibility(resultRect.Intersects(collector.mLastRect));
-
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mFirstRect);
nsLayoutUtils::TransformRect(rootFrame, canvasFrame, collector.mLastRect);
+ nsAutoTArray<nsIFrame*, 16> hitFramesInFirstRect;
+ nsLayoutUtils::GetFramesForArea(canvasFrame,
+ collector.mFirstRect,
+ hitFramesInFirstRect,
+ nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+ nsLayoutUtils::IGNORE_CROSS_DOC |
+ nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+
+ nsAutoTArray<nsIFrame*, 16> hitFramesInLastRect;
+ nsLayoutUtils::GetFramesForArea(canvasFrame,
+ collector.mLastRect,
+ hitFramesInLastRect,
+ nsLayoutUtils::IGNORE_PAINT_SUPPRESSION |
+ nsLayoutUtils::IGNORE_CROSS_DOC |
+ nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+
+ SetStartFrameVisibility(hitFramesInFirstRect.Contains(startFrame));
+ SetEndFrameVisibility(hitFramesInLastRect.Contains(endFrame));
+
SetStartFramePos(collector.mFirstRect.BottomLeft());
SetEndFramePos(collector.mLastRect.BottomRight());
SetVisibility(true);
// If range select only one character, append tilt class name to it.
bool isTilt = false;
if (startFrame && endFrame) {
// In this case <textarea>abc</textarea> and we select 'c' character,
@@ -509,53 +515,69 @@ SelectionCarets::UpdateSelectionCarets()
SetCaretDirection(mPresShell->GetSelectionCaretsStartElement(), startFrameIsRTL);
SetCaretDirection(mPresShell->GetSelectionCaretsEndElement(), !endFrameIsRTL);
SetTilted(isTilt);
}
nsresult
SelectionCarets::SelectWord()
{
- // If caret isn't visible, the word is not selectable
- if (!GetCaretVisible()) {
- return NS_OK;
- }
-
if (!mPresShell) {
return NS_OK;
}
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
if (!canvasFrame) {
return NS_OK;
}
// Find content offsets for mouse down point
nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(canvasFrame, mDownPoint,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
if (!ptFrame) {
return NS_OK;
}
+ // If frame isn't editable and we don't support non-editable fields, bail
+ // out.
+ if (!kSupportNonEditableFields && !ptFrame->GetContent()->IsEditable()) {
+ return NS_OK;
+ }
+
nsPoint ptInFrame = mDownPoint;
nsLayoutUtils::TransformPoint(canvasFrame, ptFrame, ptInFrame);
- nsIFrame* caretFocusFrame = GetCaretFocusFrame();
- if (!caretFocusFrame) {
- return NS_OK;
+ // If target frame is editable, we should move focus to targe frame. If
+ // target frame isn't editable and our focus content is editable, we should
+ // clear focus.
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
+ if (editingHost) {
+ nsCOMPtr<nsIDOMElement> elt = do_QueryInterface(editingHost->GetParent());
+ if (elt) {
+ fm->SetFocus(elt, 0);
+ }
+ } else {
+ nsIContent* focusedContent = GetFocusedContent();
+ if (focusedContent && focusedContent->GetTextEditorRootContent()) {
+ nsIDOMWindow* win = mPresShell->GetDocument()->GetWindow();
+ if (win) {
+ fm->ClearFocus(win);
+ }
+ }
}
SetSelectionDragState(true);
nsFrame* frame = static_cast<nsFrame*>(ptFrame);
nsresult rs = frame->SelectByTypeAtPoint(mPresShell->GetPresContext(), ptInFrame,
eSelectWord, eSelectWord, 0);
SetSelectionDragState(false);
// Clear maintain selection otherwise we cannot select less than a word
- nsRefPtr<nsFrameSelection> fs = caretFocusFrame->GetFrameSelection();
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
fs->MaintainSelection();
return rs;
}
/*
* If we're dragging start caret, we do not want to drag over previous
* character of end caret. Same as end caret. So we check if content offset
* exceed previous/next character of end/start caret base on aDragMode.
@@ -635,53 +657,59 @@ SelectionCarets::DragSelection(const nsP
// Find out which content we point to
nsIFrame *ptFrame = nsLayoutUtils::GetFrameForPoint(canvasFrame, movePoint,
nsLayoutUtils::IGNORE_PAINT_SUPPRESSION | nsLayoutUtils::IGNORE_CROSS_DOC);
if (!ptFrame) {
return nsEventStatus_eConsumeNoDefault;
}
- nsIFrame* caretFocusFrame = GetCaretFocusFrame();
- if (!caretFocusFrame) {
- return nsEventStatus_eConsumeNoDefault;
- }
-
- nsRefPtr<nsFrameSelection> fs = caretFocusFrame->GetFrameSelection();
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
nsresult result;
nsIFrame *newFrame = nullptr;
nsPoint newPoint;
nsPoint ptInFrame = movePoint;
nsLayoutUtils::TransformPoint(canvasFrame, ptFrame, ptInFrame);
result = fs->ConstrainFrameAndPointToAnchorSubtree(ptFrame, ptInFrame, &newFrame, newPoint);
if (NS_FAILED(result) || !newFrame) {
return nsEventStatus_eConsumeNoDefault;
}
+ bool selectable;
+ newFrame->IsSelectable(&selectable, nullptr);
+ if (!selectable) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
nsFrame::ContentOffsets offsets =
newFrame->GetContentOffsetsFromPoint(newPoint);
if (!offsets.content) {
return nsEventStatus_eConsumeNoDefault;
}
- nsISelection* caretSelection = GetSelection();
- nsRefPtr<dom::Selection> selection = static_cast<dom::Selection*>(caretSelection);
+ nsRefPtr<dom::Selection> selection = GetSelection();
if (selection->GetRangeCount() <= 0) {
return nsEventStatus_eConsumeNoDefault;
}
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
if (!CompareRangeWithContentOffset(range, fs, offsets, mDragMode)) {
return nsEventStatus_eConsumeNoDefault;
}
+ nsIFrame* anchorFrame;
+ selection->GetPrimaryFrameForAnchorNode(&anchorFrame);
+ if (!anchorFrame) {
+ return nsEventStatus_eConsumeNoDefault;
+ }
+
// Move caret postion.
nsIFrame *scrollable =
- nsLayoutUtils::GetClosestFrameOfType(caretFocusFrame, nsGkAtoms::scrollFrame);
+ nsLayoutUtils::GetClosestFrameOfType(anchorFrame, nsGkAtoms::scrollFrame);
nsWeakFrame weakScrollable = scrollable;
fs->HandleClick(offsets.content, offsets.StartOffset(),
offsets.EndOffset(),
true,
false,
offsets.associate);
if (!weakScrollable.IsAlive()) {
return nsEventStatus_eConsumeNoDefault;
@@ -696,28 +724,28 @@ SelectionCarets::DragSelection(const nsP
UpdateSelectionCarets();
return nsEventStatus_eConsumeNoDefault;
}
nscoord
SelectionCarets::GetCaretYCenterPosition()
{
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
- nsIFrame* caretFocusFrame = GetCaretFocusFrame();
- if (!canvasFrame || !caretFocusFrame) {
+ if (!canvasFrame) {
return 0;
}
- nsISelection* caretSelection = GetSelection();
- nsRefPtr<dom::Selection> selection = static_cast<dom::Selection*>(caretSelection);
+
+ nsRefPtr<dom::Selection> selection = GetSelection();
if (selection->GetRangeCount() <= 0) {
return 0;
}
+
nsRefPtr<nsRange> range = selection->GetRangeAt(0);
- nsRefPtr<nsFrameSelection> fs = caretFocusFrame->GetFrameSelection();
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
MOZ_ASSERT(mDragMode != NONE);
nsCOMPtr<nsIContent> node;
uint32_t nodeOffset;
if (mDragMode == START_FRAME) {
node = do_QueryInterface(range->GetStartParent());
nodeOffset = range->StartOffset();
} else {
@@ -737,30 +765,24 @@ SelectionCarets::GetCaretYCenterPosition
nsRect frameRect = theFrame->GetRectRelativeToSelf();
nsLayoutUtils::TransformRect(theFrame, canvasFrame, frameRect);
return frameRect.Center().y;
}
void
SelectionCarets::SetSelectionDragState(bool aState)
{
- nsIFrame* caretFocusFrame = GetCaretFocusFrame();
- if (!caretFocusFrame) {
- return;
- }
-
- nsRefPtr<nsFrameSelection> fs = caretFocusFrame->GetFrameSelection();
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
fs->SetDragState(aState);
}
void
SelectionCarets::SetSelectionDirection(bool aForward)
{
- nsISelection* caretSelection = GetSelection();
- nsRefPtr<dom::Selection> selection = static_cast<dom::Selection*>(caretSelection);
+ nsRefPtr<dom::Selection> selection = GetSelection();
selection->SetDirection(aForward ? eDirNext : eDirPrevious);
}
static void
SetFramePos(dom::Element* aElement, const nsPoint& aPosition)
{
if (!aElement) {
return;
@@ -815,48 +837,50 @@ SelectionCarets::GetStartFrameRect()
nsRect
SelectionCarets::GetEndFrameRect()
{
dom::Element* element = mPresShell->GetSelectionCaretsEndElement();
nsIFrame* canvasFrame = mPresShell->GetCanvasFrame();
return nsLayoutUtils::GetRectRelativeToFrame(element, canvasFrame);
}
-nsIFrame*
-SelectionCarets::GetCaretFocusFrame()
+nsIContent*
+SelectionCarets::GetFocusedContent()
{
- nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
- if (!caret) {
- return nullptr;
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ return fm->GetFocusedContent();
}
- nsRect focusRect;
- return caret->GetGeometry(&focusRect);
+ return nullptr;
}
-bool
-SelectionCarets::GetCaretVisible()
+Selection*
+SelectionCarets::GetSelection()
{
- if (!mPresShell) {
- return false;
+ nsRefPtr<nsFrameSelection> fs = GetFrameSelection();
+ if (fs) {
+ return fs->GetSelection(nsISelectionController::SELECTION_NORMAL);
}
-
- nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
- if (!caret) {
- return false;
- }
-
- return caret->IsVisible();
+ return nullptr;
}
-nsISelection*
-SelectionCarets::GetSelection()
+already_AddRefed<nsFrameSelection>
+SelectionCarets::GetFrameSelection()
{
- nsRefPtr<nsCaret> caret = mPresShell->GetCaret();
- return caret->GetSelection();
+ nsIContent* focusNode = GetFocusedContent();
+ if (focusNode) {
+ nsIFrame* focusFrame = focusNode->GetPrimaryFrame();
+ if (!focusFrame) {
+ return nullptr;
+ }
+ return focusFrame->GetFrameSelection();
+ } else {
+ return mPresShell->FrameSelection();
+ }
}
nsresult
SelectionCarets::NotifySelectionChanged(nsIDOMDocument* aDoc,
nsISelection* aSel,
int16_t aReason)
{
bool isCollapsed;