Reviewed by Darin Adler.
- preparation for https://bugs.webkit.org/show_bug.cgi?id=3729
<rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
The three main changes in this patch are:
1) Making all inline boxes know their bidi level, instead of just text
boxes knowing whether their bidi level is odd or even. This is
required for the next change.
2) Replacing RenderObject::inlineBox() with
Position::getInlineBoxAndOffset() in recognition of the fact that the
inline box containing the primary caret for a position in a given
node may belong to a different node's renderer.
3) Changing RenderObject::caretRect() to take an InlineBox parameter,
and changing callers to call VisiblePosition::caretRect(), which
locates the inline box, then calls caretRect() on the renderer for
that box. This, combined with the previous change, ensures that the
primary caret is rendered at the right place for positions that
lie on a directionality boundary.
Test: platform/mac/editing/input/caret-primary-bidi.html
* WebCore.base.exp: Added the VisiblePosition(Node*, int, EAffinity)
constructor and VisiblePosition::caretRect(), and sorted.
* dom/Position.cpp:
(WebCore::nextRenderedEditable): Adjusted for the removal of
RenderObject::inlineBox().
(WebCore::previousRenderedEditable): Ditto.
(WebCore::Position::rendersInDifferentPosition): Ditto.
(WebCore::Position::getInlineBoxAndOffset): Added. Gets the inline box
and the offset within that box at which the primary caret for this
position should render.
* dom/Position.h:
* editing/DeleteSelectionCommand.cpp:
(WebCore::DeleteSelectionCommand::mergeParagraphs): Changed to call
VisiblePosition::caretRect() instead of calling the RenderObject method.
* editing/SelectionController.cpp:
(WebCore::caretY): Ditto.
(WebCore::SelectionController::xPosForVerticalArrowNavigation): Ditto.
(WebCore::SelectionController::layout): Ditto.
* editing/VisiblePosition.cpp:
(WebCore::VisiblePosition::caretRect): Changed to call
getInlineBoxAndOffset() to get the correct inline box and call the
renderer for that box.
* editing/VisiblePosition.h:
(WebCore::VisiblePosition::getInlineBoxAndOffset): Added convenience
methods for getting the inline box and caret offset for a visible
position, accounting for its affinity.
* editing/visible_units.cpp:
(WebCore::rootBoxForLine): Changed to use getInlineBoxAndOffset()
instead of RenderObject::inlineBox().
(WebCore::startPositionForLine):
(WebCore::endPositionForLine):
(WebCore::previousLinePosition): Ditto.
(WebCore::nextLinePosition): Ditto.
* page/AccessibilityObject.cpp:
(WebCore::updateAXLineStartForVisiblePosition): Ditto.
* page/Frame.cpp:
(WebCore::Frame::firstRectForRange): Ditto.
* rendering/InlineBox.cpp:
(WebCore::InlineBox::caretMinOffset): Changed to forward to the
renderer.
(WebCore::InlineBox::caretMaxOffset): Ditto.
* rendering/InlineBox.h: Replaced the m_reversed bit, intended for use
in InlineTextBox only, with six bits of the bidi level of the box,
intended for use in all leaf inline boxes.
(WebCore::InlineBox::InlineBox): Added missing initializer for
m_dirOverride and initialized the bidi level.
(WebCore::InlineBox::bidiLevel): Added this accessor.
(WebCore::InlineBox::setBidiLevel): Ditto.
(WebCore::InlineBox::direction): Ditto.
(WebCore::InlineBox::caretLeftmostOffset): Added this convenience
method.
(WebCore::InlineBox::caretRightmostOffset): Ditto.
* rendering/InlineTextBox.cpp: Replaced all references to m_reversed
with checking of direction().
(WebCore::InlineTextBox::selectionRect):
(WebCore::InlineTextBox::placeEllipsisBox):
(WebCore::InlineTextBox::paint):
(WebCore::InlineTextBox::paintSelection):
(WebCore::InlineTextBox::paintCompositionBackground):
(WebCore::InlineTextBox::paintSpellingOrGrammarMarker):
(WebCore::InlineTextBox::paintTextMatchMarker):
(WebCore::InlineTextBox::textPos):
(WebCore::InlineTextBox::offsetForPosition):
(WebCore::InlineTextBox::positionForOffset):
* rendering/RenderBR.cpp: Removed inlineBox().
* rendering/RenderBR.h: Ditto.
* rendering/RenderBox.cpp:
(WebCore::RenderBox::caretRect): Changed to take an inline box and
account for the direction of the box (or the renderer) in positioning
the caret: in right-to-left boxes, the "before" position is to the right
while "after" is to the left.
* rendering/RenderBox.h:
* rendering/RenderFlow.cpp:
(WebCore::RenderFlow::caretRect): Updated the signature.
* rendering/RenderFlow.h:
* rendering/RenderObject.cpp:
(WebCore::RenderObject::caretRect): Updated the signature.
(WebCore::RenderObject::caretMaxOffset): Changed to return the child
node count (or 1 if there are no children) for replaced elements, such
as <select>s.
* rendering/RenderObject.h:
* rendering/RenderReplaced.cpp: Removed caretMinOffset() and
caretMaxOffset() because the base class implementation does the right
thing for replaced objects now.
* rendering/RenderReplaced.h:
* rendering/RenderSVGInlineText.cpp:
(WebCore::RenderSVGInlineText::caretRect): Updated the signature.
(WebCore::RenderSVGInlineText::positionForCoordinates): Updated for
the change from m_reversed to direction().
* rendering/RenderSVGInlineText.h:
* rendering/RenderText.cpp:
(WebCore::RenderText::caretRect): Changed to take an inline box and
removed the code that used to find the inline for the given position.
Changed use of m_reversed to use direction().
(WebCore::RenderText::position): Changed use of m_reversed to use
direction().
* rendering/RenderText.h:
* rendering/RenderTextControl.cpp:
(WebCore::RenderTextControl::textWithHardLineBreaks): Adjusted for the
removal of RenderObject::inlineBox().
* rendering/RenderTreeAsText.cpp:
(WebCore::writeTextRun): Changed to use direction() instead of
m_reversed.
* rendering/SVGInlineTextBox.cpp: Ditto.
(WebCore::SVGInlineTextBox::calculateGlyphBoundaries):
(WebCore::SVGInlineTextBoxClosestCharacterToPositionWalker::chunkPortionCallback):
(WebCore::SVGInlineTextBox::svgCharacterHitsPosition):
* rendering/SVGRenderTreeAsText.cpp: Ditto.
(WebCore::writeSVGInlineTextBox):
* rendering/SVGRootInlineBox.cpp: Ditto.
(WebCore::svgTextRunForInlineTextBox):
(WebCore::cummulatedWidthOrHeightOfTextChunk):
(WebCore::SVGRootInlineBox::buildLayoutInformationForTextBox):
* rendering/bidi.cpp:
(WebCore::RenderBlock::constructLine): Made this function set the
bidi level on all leaf boxes.
* svg/SVGTextContentElement.cpp: Changed to use direction() instead of
m_reversed.
(WebCore::cumulativeCharacterRangeLength):
(WebCore::SVGInlineTextBoxQueryWalker::chunkPortionCallback):
WebKit/mac:
Reviewed by Darin Adler.
- preparation for https://bugs.webkit.org/show_bug.cgi?id=3729
<rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
* WebView/WebFrame.mm:
(-[WebFrame _caretRectAtNode:offset:affinity:]): Changed to use
VisiblePosition::caretRect() instead of the RenderObject method which
was removed.
LayoutTests:
Reviewed by Darin Adler.
- test the visual position of the primary caret in bidirectional text
* platform/mac/editing/input/caret-primary-bidi-expected.txt: Added.
* platform/mac/editing/input/caret-primary-bidi.html: Added.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@32508
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2008-04-24 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ - test the visual position of the primary caret in bidirectional text
+
+ * platform/mac/editing/input/caret-primary-bidi-expected.txt: Added.
+ * platform/mac/editing/input/caret-primary-bidi.html: Added.
+
2008-04-24 Justin Garcia <justin.garcia@apple.com>
Reviewed by Darin Adler.
--- /dev/null
+0: -1,572,0,28
+1: -1,572,0,28
+2: 8,478,0,28
+3: 49,564,0,28
+4: 157,564,0,28
+5: 143,564,0,28
+6: 86,564,0,28
+7: 101,564,0,28
+8: 116,564,0,28
+9: 132,564,0,28
+10: 74,564,0,28
+11: 57,564,0,28
+12: 172,564,0,28
+13: 187,564,0,28
+14: 200,564,0,28
+15: 210,564,0,28
+16: 8,536,0,28
+17: 116,536,0,28
+18: 102,536,0,28
+19: 45,536,0,28
+20: 60,536,0,28
+21: 75,536,0,28
+22: 91,536,0,28
+23: 33,536,0,28
+24: 16,536,0,28
+25: 131,536,0,28
+26: 8,508,0,28
+27: 150,508,0,28
+28: 136,508,0,28
+29: 125,508,0,28
+30: 33,508,0,28
+31: 16,508,0,28
+32: 165,508,0,28
+33: 8,478,0,28
+34: 33,478,0,28
+35: 19,478,0,28
+36: 48,478,0,28
+37: 61,478,0,28
+38: 76,478,0,28
+39: 89,478,0,28
+40: 114,478,0,28
+41: 97,478,0,28
+42: 126,478,0,28
+43: 792,450,0,28
+44: 764,450,0,28
+45: 779,450,0,28
+46: 751,450,0,28
+47: 736,450,0,28
+48: 722,450,0,28
+49: 711,450,0,28
+50: 680,450,0,28
+51: 695,450,0,28
+52: 665,450,0,28
+53: 653,450,0,28
+54: 636,450,0,28
+55: 628,450,0,28
+56: 605,450,0,28
+57: 618,450,0,28
+58: 590,450,0,28
+59: 792,422,0,28
+60: 777,422,0,28
+61: 763,422,0,28
+62: 752,422,0,28
+63: 721,422,0,28
+64: 736,422,0,28
+65: 706,422,0,28
+66: 694,422,0,28
+67: 677,422,0,28
+68: 669,422,0,28
+69: 792,394,0,28
+70: 777,394,0,28
+71: 763,394,0,28
+72: 752,394,0,28
+73: 660,394,0,28
+74: 643,394,0,28
+75: 635,394,0,28
+76: 792,364,0,28
+77: 764,364,0,28
+78: 779,364,0,28
+79: 751,364,0,28
+80: 736,364,0,28
+81: 722,364,0,28
+82: 711,364,0,28
+83: 688,364,0,28
+84: 701,364,0,28
+85: 673,364,0,28
+
--- /dev/null
+<style>
+ div#tests { -webkit-user-modify: read-write; font-family: Lucida Grande; font-size: 24px; }
+</style>
+<div id="tests">
+ <div>
+ abcאבג123דהוdef
+ </div>
+ <div>
+ אבג123דהו
+ </div>
+ <div>
+ אבג<select></select><select></select>דהו
+ </div>
+ <div>
+ אבגabcדהו
+ </div>
+ <div style="direction: rtl;">
+ abcאבג123דהוdef
+ </div>
+ <div style="direction: rtl;">
+ אבג123דהו
+ </div>
+ <div style="direction: rtl;">
+ אבג<select></select><select></select>דהו
+ </div>
+ <div style="direction: rtl;">
+ abcאבגdef
+ </div>
+</div>
+<script>
+ if (window.layoutTestController) {
+ layoutTestController.dumpAsText();
+
+ var log = "";
+
+ for (var i = 0; i < 86; ++i)
+ log += i + ": " + textInputController.firstRectForCharacterRange(i, 0) + "\n";
+
+ document.body.appendChild(document.createElement("pre")).appendChild(document.createTextNode(log));
+ document.getElementById("tests").style.display = "none";
+ }
+</script>
+2008-04-24 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ - preparation for https://bugs.webkit.org/show_bug.cgi?id=3729
+ <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
+
+ The three main changes in this patch are:
+
+ 1) Making all inline boxes know their bidi level, instead of just text
+ boxes knowing whether their bidi level is odd or even. This is
+ required for the next change.
+
+ 2) Replacing RenderObject::inlineBox() with
+ Position::getInlineBoxAndOffset() in recognition of the fact that the
+ inline box containing the primary caret for a position in a given
+ node may belong to a different node's renderer.
+
+ 3) Changing RenderObject::caretRect() to take an InlineBox parameter,
+ and changing callers to call VisiblePosition::caretRect(), which
+ locates the inline box, then calls caretRect() on the renderer for
+ that box. This, combined with the previous change, ensures that the
+ primary caret is rendered at the right place for positions that
+ lie on a directionality boundary.
+
+ Test: platform/mac/editing/input/caret-primary-bidi.html
+
+ * WebCore.base.exp: Added the VisiblePosition(Node*, int, EAffinity)
+ constructor and VisiblePosition::caretRect(), and sorted.
+
+ * dom/Position.cpp:
+ (WebCore::nextRenderedEditable): Adjusted for the removal of
+ RenderObject::inlineBox().
+ (WebCore::previousRenderedEditable): Ditto.
+ (WebCore::Position::rendersInDifferentPosition): Ditto.
+ (WebCore::Position::getInlineBoxAndOffset): Added. Gets the inline box
+ and the offset within that box at which the primary caret for this
+ position should render.
+
+ * dom/Position.h:
+
+ * editing/DeleteSelectionCommand.cpp:
+ (WebCore::DeleteSelectionCommand::mergeParagraphs): Changed to call
+ VisiblePosition::caretRect() instead of calling the RenderObject method.
+
+ * editing/SelectionController.cpp:
+ (WebCore::caretY): Ditto.
+ (WebCore::SelectionController::xPosForVerticalArrowNavigation): Ditto.
+ (WebCore::SelectionController::layout): Ditto.
+
+ * editing/VisiblePosition.cpp:
+ (WebCore::VisiblePosition::caretRect): Changed to call
+ getInlineBoxAndOffset() to get the correct inline box and call the
+ renderer for that box.
+
+ * editing/VisiblePosition.h:
+ (WebCore::VisiblePosition::getInlineBoxAndOffset): Added convenience
+ methods for getting the inline box and caret offset for a visible
+ position, accounting for its affinity.
+
+ * editing/visible_units.cpp:
+ (WebCore::rootBoxForLine): Changed to use getInlineBoxAndOffset()
+ instead of RenderObject::inlineBox().
+ (WebCore::startPositionForLine):
+ (WebCore::endPositionForLine):
+ (WebCore::previousLinePosition): Ditto.
+ (WebCore::nextLinePosition): Ditto.
+
+ * page/AccessibilityObject.cpp:
+ (WebCore::updateAXLineStartForVisiblePosition): Ditto.
+
+ * page/Frame.cpp:
+ (WebCore::Frame::firstRectForRange): Ditto.
+
+ * rendering/InlineBox.cpp:
+ (WebCore::InlineBox::caretMinOffset): Changed to forward to the
+ renderer.
+ (WebCore::InlineBox::caretMaxOffset): Ditto.
+ * rendering/InlineBox.h: Replaced the m_reversed bit, intended for use
+ in InlineTextBox only, with six bits of the bidi level of the box,
+ intended for use in all leaf inline boxes.
+ (WebCore::InlineBox::InlineBox): Added missing initializer for
+ m_dirOverride and initialized the bidi level.
+ (WebCore::InlineBox::bidiLevel): Added this accessor.
+ (WebCore::InlineBox::setBidiLevel): Ditto.
+ (WebCore::InlineBox::direction): Ditto.
+ (WebCore::InlineBox::caretLeftmostOffset): Added this convenience
+ method.
+ (WebCore::InlineBox::caretRightmostOffset): Ditto.
+
+ * rendering/InlineTextBox.cpp: Replaced all references to m_reversed
+ with checking of direction().
+ (WebCore::InlineTextBox::selectionRect):
+ (WebCore::InlineTextBox::placeEllipsisBox):
+ (WebCore::InlineTextBox::paint):
+ (WebCore::InlineTextBox::paintSelection):
+ (WebCore::InlineTextBox::paintCompositionBackground):
+ (WebCore::InlineTextBox::paintSpellingOrGrammarMarker):
+ (WebCore::InlineTextBox::paintTextMatchMarker):
+ (WebCore::InlineTextBox::textPos):
+ (WebCore::InlineTextBox::offsetForPosition):
+ (WebCore::InlineTextBox::positionForOffset):
+
+ * rendering/RenderBR.cpp: Removed inlineBox().
+ * rendering/RenderBR.h: Ditto.
+
+ * rendering/RenderBox.cpp:
+ (WebCore::RenderBox::caretRect): Changed to take an inline box and
+ account for the direction of the box (or the renderer) in positioning
+ the caret: in right-to-left boxes, the "before" position is to the right
+ while "after" is to the left.
+ * rendering/RenderBox.h:
+
+ * rendering/RenderFlow.cpp:
+ (WebCore::RenderFlow::caretRect): Updated the signature.
+ * rendering/RenderFlow.h:
+
+ * rendering/RenderObject.cpp:
+ (WebCore::RenderObject::caretRect): Updated the signature.
+ (WebCore::RenderObject::caretMaxOffset): Changed to return the child
+ node count (or 1 if there are no children) for replaced elements, such
+ as <select>s.
+ * rendering/RenderObject.h:
+
+ * rendering/RenderReplaced.cpp: Removed caretMinOffset() and
+ caretMaxOffset() because the base class implementation does the right
+ thing for replaced objects now.
+ * rendering/RenderReplaced.h:
+
+ * rendering/RenderSVGInlineText.cpp:
+ (WebCore::RenderSVGInlineText::caretRect): Updated the signature.
+ (WebCore::RenderSVGInlineText::positionForCoordinates): Updated for
+ the change from m_reversed to direction().
+ * rendering/RenderSVGInlineText.h:
+
+ * rendering/RenderText.cpp:
+ (WebCore::RenderText::caretRect): Changed to take an inline box and
+ removed the code that used to find the inline for the given position.
+ Changed use of m_reversed to use direction().
+ (WebCore::RenderText::position): Changed use of m_reversed to use
+ direction().
+ * rendering/RenderText.h:
+
+ * rendering/RenderTextControl.cpp:
+ (WebCore::RenderTextControl::textWithHardLineBreaks): Adjusted for the
+ removal of RenderObject::inlineBox().
+
+ * rendering/RenderTreeAsText.cpp:
+ (WebCore::writeTextRun): Changed to use direction() instead of
+ m_reversed.
+
+ * rendering/SVGInlineTextBox.cpp: Ditto.
+ (WebCore::SVGInlineTextBox::calculateGlyphBoundaries):
+ (WebCore::SVGInlineTextBoxClosestCharacterToPositionWalker::chunkPortionCallback):
+ (WebCore::SVGInlineTextBox::svgCharacterHitsPosition):
+
+ * rendering/SVGRenderTreeAsText.cpp: Ditto.
+ (WebCore::writeSVGInlineTextBox):
+
+ * rendering/SVGRootInlineBox.cpp: Ditto.
+ (WebCore::svgTextRunForInlineTextBox):
+ (WebCore::cummulatedWidthOrHeightOfTextChunk):
+ (WebCore::SVGRootInlineBox::buildLayoutInformationForTextBox):
+
+ * rendering/bidi.cpp:
+ (WebCore::RenderBlock::constructLine): Made this function set the
+ bidi level on all leaf boxes.
+
+ * svg/SVGTextContentElement.cpp: Changed to use direction() instead of
+ m_reversed.
+ (WebCore::cumulativeCharacterRangeLength):
+ (WebCore::SVGInlineTextBoxQueryWalker::chunkPortionCallback):
+
2008-04-24 Sam Weinig <sam@webkit.org>
Fix the world.
__ZN7WebCore15StringTruncator13rightTruncateERKNS_6StringEfRKNS_4FontEb
__ZN7WebCore15StringTruncator14centerTruncateERKNS_6StringEfRKNS_4FontEb
__ZN7WebCore15StringTruncator5widthERKNS_6StringERKNS_4FontEb
+__ZN7WebCore15VisiblePositionC1EPNS_4NodeEiNS_9EAffinityE
__ZN7WebCore15VisiblePositionC1ERKNS_8PositionENS_9EAffinityE
__ZN7WebCore16FontPlatformDataC1EP6NSFontbb
__ZN7WebCore16FontPlatformDataD1Ev
__ZNK7WebCore11FrameLoader16outgoingReferrerEv
__ZNK7WebCore11FrameLoader16responseMIMETypeEv
__ZNK7WebCore11FrameLoader20activeDocumentLoaderEv
-__ZNK7WebCore11FrameLoader25provisionalDocumentLoaderEv
__ZNK7WebCore11FrameLoader21isQuickRedirectComingEv
+__ZNK7WebCore11FrameLoader25provisionalDocumentLoaderEv
__ZNK7WebCore11FrameLoader27numPendingOrLoadingRequestsEb
__ZNK7WebCore11FrameLoader6clientEv
__ZNK7WebCore11FrameLoader8loadTypeEv
__ZNK7WebCore15VisiblePosition14characterAfterEv
__ZNK7WebCore15VisiblePosition4nextEb
__ZNK7WebCore15VisiblePosition8previousEb
+__ZNK7WebCore15VisiblePosition9caretRectEv
__ZNK7WebCore16ResourceResponse13nsURLResponseEv
__ZNK7WebCore17ResourceErrorBase8lazyInitEv
__ZNK7WebCore19InspectorController17drawNodeHighlightERNS_15GraphicsContextE
node = node->nextEditable();
if (!node)
return 0;
- if (!node->renderer())
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
continue;
- if (node->renderer()->inlineBox(0))
+ if (renderer->inlineBoxWrapper() || renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox())
return node;
}
return 0;
node = node->previousEditable();
if (!node)
return 0;
- if (!node->renderer())
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
continue;
- if (node->renderer()->inlineBox(0))
+ if (renderer->inlineBoxWrapper() || renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox())
return node;
}
return 0;
if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
return false;
- LOG(Editing, "renderer: %p [%p]\n", renderer, renderer ? renderer->inlineBox(offset()) : 0);
+ int ignoredCaretOffset;
+ InlineBox* b1;
+ getInlineBoxAndOffset(DOWNSTREAM, b1, ignoredCaretOffset);
+ InlineBox* b2;
+ pos.getInlineBoxAndOffset(DOWNSTREAM, b2, ignoredCaretOffset);
+
+ LOG(Editing, "renderer: %p [%p]\n", renderer, b1);
LOG(Editing, "thisRenderedOffset: %d\n", thisRenderedOffset);
- LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, posRenderer ? posRenderer->inlineBox(offset()) : 0);
+ LOG(Editing, "posRenderer: %p [%p]\n", posRenderer, b2);
LOG(Editing, "posRenderedOffset: %d\n", posRenderedOffset);
LOG(Editing, "node min/max: %d:%d\n", caretMinOffset(node()), caretMaxRenderedOffset(node()));
LOG(Editing, "pos node min/max: %d:%d\n", caretMinOffset(pos.node()), caretMaxRenderedOffset(pos.node()));
LOG(Editing, "----------------------------------------------------------------------\n");
- InlineBox *b1 = renderer ? renderer->inlineBox(offset()) : 0;
- InlineBox *b2 = posRenderer ? posRenderer->inlineBox(pos.offset()) : 0;
-
if (!b1 || !b2) {
return false;
}
return Position();
}
+void Position::getInlineBoxAndOffset(EAffinity affinity, InlineBox*& inlineBox, int& caretOffset) const
+{
+ TextDirection primaryDirection = LTR;
+ for (RenderObject* r = node()->renderer(); r; r = r->parent()) {
+ if (r->isBlockFlow()) {
+ primaryDirection = r->style()->direction();
+ break;
+ }
+ }
+ getInlineBoxAndOffset(affinity, primaryDirection, inlineBox, caretOffset);
+}
+
+void Position::getInlineBoxAndOffset(EAffinity affinity, TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const
+{
+ caretOffset = offset();
+ RenderObject* renderer = node()->renderer();
+ if (!renderer->isText()) {
+ inlineBox = renderer->inlineBoxWrapper();
+ if (!inlineBox || caretOffset > inlineBox->caretMinOffset() && caretOffset < inlineBox->caretMaxOffset())
+ return;
+ } else {
+ RenderText* textRenderer = static_cast<RenderText*>(renderer);
+
+ InlineTextBox* box;
+ InlineTextBox* candidate = 0;
+
+ for (box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+ int caretMinOffset = box->caretMinOffset();
+ int caretMaxOffset = box->caretMaxOffset();
+
+ if (caretOffset < caretMinOffset || caretOffset > caretMaxOffset)
+ continue;
+
+ if (caretOffset > caretMinOffset && caretOffset < caretMaxOffset) {
+ inlineBox = box;
+ return;
+ }
+
+ if (caretOffset == caretMinOffset ^ affinity == UPSTREAM)
+ break;
+
+ candidate = box;
+ }
+ inlineBox = box ? box : candidate;
+ }
+
+ if (!inlineBox)
+ return;
+
+ unsigned char level = inlineBox->bidiLevel();
+
+ if (inlineBox->direction() == primaryDirection) {
+ if (caretOffset == inlineBox->caretRightmostOffset()) {
+ InlineBox* nextBox = inlineBox->nextLeafChild();
+ if (!nextBox || nextBox->bidiLevel() >= level)
+ return;
+
+ level = nextBox->bidiLevel();
+ InlineBox* prevBox = inlineBox;
+ do {
+ prevBox = prevBox->prevLeafChild();
+ } while (prevBox && prevBox->bidiLevel() > level);
+
+ if (prevBox && prevBox->bidiLevel() == level) // For example, abc FED 123 ^ CBA
+ return;
+
+ // For example, abc 123 ^ CBA
+ while (InlineBox* nextBox = inlineBox->nextLeafChild()) {
+ if (nextBox->bidiLevel() < level)
+ break;
+ inlineBox = nextBox;
+ }
+ caretOffset = inlineBox->caretRightmostOffset();
+ } else {
+ InlineBox* prevBox = inlineBox->prevLeafChild();
+ if (!prevBox || prevBox->bidiLevel() >= level)
+ return;
+
+ level = prevBox->bidiLevel();
+ InlineBox* nextBox = inlineBox;
+ do {
+ nextBox = nextBox->nextLeafChild();
+ } while (nextBox && nextBox->bidiLevel() > level);
+
+ if (nextBox && nextBox->bidiLevel() == level)
+ return;
+
+ while (InlineBox* prevBox = inlineBox->prevLeafChild()) {
+ if (prevBox->bidiLevel() < level)
+ break;
+ inlineBox = prevBox;
+ }
+ caretOffset = inlineBox->caretLeftmostOffset();
+ }
+ return;
+ }
+
+ if (caretOffset == inlineBox->caretLeftmostOffset()) {
+ InlineBox* prevBox = inlineBox->prevLeafChild();
+ if (!prevBox || prevBox->bidiLevel() < level) {
+ // Left edge of a secondary run. Set to the right edge of the entire run.
+ while (InlineBox* nextBox = inlineBox->nextLeafChild()) {
+ if (nextBox->bidiLevel() < level)
+ break;
+ inlineBox = nextBox;
+ }
+ caretOffset = inlineBox->caretRightmostOffset();
+ } else if (prevBox->bidiLevel() > level) {
+ // Right edge of a "tertiary" run. Set to the left edge of that run.
+ while (InlineBox* tertiaryBox = inlineBox->prevLeafChild()) {
+ if (tertiaryBox->bidiLevel() <= level)
+ break;
+ inlineBox = tertiaryBox;
+ }
+ caretOffset = !inlineBox->caretLeftmostOffset();
+ }
+ } else {
+ InlineBox* nextBox = inlineBox->nextLeafChild();
+ if (!nextBox || nextBox->bidiLevel() < level) {
+ // Right edge of a secondary run. Set to the left edge of the entire run.
+ while (InlineBox* prevBox = inlineBox->prevLeafChild()) {
+ if (prevBox->bidiLevel() < level)
+ break;
+ inlineBox = prevBox;
+ }
+ caretOffset = inlineBox->caretLeftmostOffset();
+ } else if (nextBox->bidiLevel() > level) {
+ // Left edge of a "tertiary" run. Set to the right edge of that run.
+ while (InlineBox* tertiaryBox = inlineBox->nextLeafChild()) {
+ if (tertiaryBox->bidiLevel() <= level)
+ break;
+ inlineBox = tertiaryBox;
+ }
+ caretOffset = inlineBox->caretRightmostOffset();
+ }
+ }
+}
+
void Position::debugPosition(const char* msg) const
{
if (isNull())
#define Position_h
#include "TextAffinity.h"
+#include "TextDirection.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefPtr.h>
class CSSComputedStyleDeclaration;
class Element;
+class InlineBox;
class Node;
class Range;
class RenderObject;
bool inRenderedText() const;
bool isRenderedCharacter() const;
bool rendersInDifferentPosition(const Position&) const;
-
+
+ void getInlineBoxAndOffset(EAffinity, InlineBox*&, int& caretOffset) const;
+ void getInlineBoxAndOffset(EAffinity, TextDirection primaryDirection, InlineBox*&, int& caretOffset) const;
+
static bool hasRenderedNonAnonymousDescendantsWithHeight(RenderObject*);
static bool nodeIsUserSelectNone(Node*);
// The rule for merging into an empty block is: only do so if its farther to the right.
// FIXME: Consider RTL.
// FIXME: handleSpecialCaseBRDelete prevents us from getting here in a case like <ul><li>foo<br><br></li></ul>^foo
- if (isStartOfParagraph(mergeDestination) &&
- startOfParagraphToMove.deepEquivalent().node()->renderer()->caretRect(startOfParagraphToMove.deepEquivalent().offset()).location().x() >
- mergeDestination.deepEquivalent().node()->renderer()->caretRect(startOfParagraphToMove.deepEquivalent().offset()).location().x()) {
+ if (isStartOfParagraph(mergeDestination) && startOfParagraphToMove.caretRect().x() > mergeDestination.caretRect().x()) {
ASSERT(mergeDestination.deepEquivalent().downstream().node()->hasTagName(brTag));
removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downstream().node());
m_endingPosition = startOfParagraphToMove.deepEquivalent();
// FIXME: Maybe baseline would be better?
static bool caretY(const VisiblePosition &c, int &y)
{
- Position p = c.deepEquivalent();
- Node *n = p.node();
- if (!n)
- return false;
- RenderObject *r = p.node()->renderer();
- if (!r)
- return false;
- IntRect rect = r->caretRect(p.offset());
+ IntRect rect = c.caretRect();
if (rect.isEmpty())
return false;
y = rect.y() + rect.height() / 2;
return x;
if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) {
- pos = VisiblePosition(pos, m_sel.affinity()).deepEquivalent();
+ VisiblePosition visiblePosition(pos, m_sel.affinity());
// VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
// after the selection is created and before this function is called.
- x = pos.isNotNull() ? pos.node()->renderer()->caretRect(pos.offset(), m_sel.affinity()).x() : 0;
+ x = visiblePosition.isNotNull() ? visiblePosition.caretRect().x() : 0;
m_xPosForVerticalArrowNavigation = x;
}
else
m_caretPositionOnLayout = IntPoint();
if (isCaret()) {
- Position pos = m_sel.start();
- pos = VisiblePosition(m_sel.start(), m_sel.affinity()).deepEquivalent();
+ VisiblePosition pos(m_sel.start(), m_sel.affinity());
if (pos.isNotNull()) {
- ASSERT(pos.node()->renderer());
- m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), m_sel.affinity());
-
+ ASSERT(pos.deepEquivalent().node()->renderer());
+ m_caretRect = pos.caretRect();
+
int x, y;
- pos.node()->renderer()->absolutePositionForContent(x, y);
+ pos.deepEquivalent().node()->renderer()->absolutePositionForContent(x, y);
m_caretPositionOnLayout = IntPoint(x, y);
}
}
IntRect VisiblePosition::caretRect() const
{
- if (!m_deepPosition.node() || !m_deepPosition.node()->renderer())
+ Node* node = m_deepPosition.node();
+ if (!node)
+ return IntRect();
+
+ RenderObject* renderer = node->renderer();
+ if (!renderer)
return IntRect();
- return m_deepPosition.node()->renderer()->caretRect(m_deepPosition.offset(), m_affinity);
+ InlineBox* inlineBox;
+ int caretOffset;
+ getInlineBoxAndOffset(inlineBox, caretOffset);
+
+ if (inlineBox)
+ renderer = inlineBox->object();
+
+ return renderer->caretRect(inlineBox, caretOffset);
}
void VisiblePosition::debugPosition(const char* msg) const
#include "Node.h"
#include "Position.h"
+#include "TextDirection.h"
namespace WebCore {
// position is not at a line break.
#define VP_UPSTREAM_IF_POSSIBLE UPSTREAM
+class InlineBox;
+
class VisiblePosition {
public:
// NOTE: UPSTREAM affinity will be used only if pos is at end of a wrapped line,
Element* rootEditableElement() const { return m_deepPosition.isNotNull() ? m_deepPosition.node()->rootEditableElement() : 0; }
+ void getInlineBoxAndOffset(InlineBox*& inlineBox, int& caretOffset) const
+ {
+ m_deepPosition.getInlineBoxAndOffset(m_affinity, inlineBox, caretOffset);
+ }
+
+ void getInlineBoxAndOffset(TextDirection primaryDirection, InlineBox*& inlineBox, int& caretOffset) const
+ {
+ m_deepPosition.getInlineBoxAndOffset(m_affinity, primaryDirection, inlineBox, caretOffset);
+ }
+
IntRect caretRect() const;
#ifndef NDEBUG
private:
void init(const Position&, EAffinity);
Position canonicalPosition(const Position&);
-
+
Position m_deepPosition;
EAffinity m_affinity;
};
RenderObject *renderer = node->renderer();
if (!renderer)
return 0;
+
+ InlineBox* box;
+ int offset;
+ c.getInlineBoxAndOffset(box, offset);
- InlineBox *box = renderer->inlineBox(p.offset(), c.affinity());
- if (!box)
- return 0;
-
- return box->root();
+ return box ? box->root() : 0;
}
static VisiblePosition positionAvoidingFirstPositionInTable(const VisiblePosition& c)
{
if (c.isNull())
return VisiblePosition();
-
+
RootInlineBox *rootBox = rootBoxForLine(c);
if (!rootBox) {
// There are VisiblePositions at offset 0 in blocks without
{
if (c.isNull())
return VisiblePosition();
-
+
RootInlineBox *rootBox = rootBoxForLine(c);
if (!rootBox) {
// There are VisiblePositions at offset 0 in blocks without
RenderBlock *containingBlock = 0;
RootInlineBox *root = 0;
- InlineBox *box = renderer->inlineBox(p.offset(), visiblePosition.affinity());
+ InlineBox* box;
+ int ignoredCaretOffset;
+ visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
if (box) {
root = box->root()->prevRootBox();
if (root)
Position pos(n, caretMinOffset(n));
if (pos.isCandidate()) {
ASSERT(n->renderer());
- box = n->renderer()->inlineBox(caretMaxOffset(n));
+ Position maxPos(n, caretMaxOffset(n));
+ maxPos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
if (box) {
// previous root line box found
root = box->root();
RenderBlock *containingBlock = 0;
RootInlineBox *root = 0;
- InlineBox *box = renderer->inlineBox(p.offset(), visiblePosition.affinity());
+ InlineBox* box;
+ int ignoredCaretOffset;
+ visiblePosition.getInlineBoxAndOffset(box, ignoredCaretOffset);
if (box) {
root = box->root()->nextRootBox();
if (root)
Position pos(n, caretMinOffset(n));
if (pos.isCandidate()) {
ASSERT(n->renderer());
- box = n->renderer()->inlineBox(caretMinOffset(n));
+ pos.getInlineBoxAndOffset(DOWNSTREAM, box, ignoredCaretOffset);
if (box) {
// next root line box found
root = box->root();
if (!p.node())
break;
renderer = p.node()->renderer();
- if (!renderer || renderer->inlineBox(p.offset(), tempPosition.affinity()) || (renderer->isRenderBlock() && p.offset() == 0))
+ if (!renderer || renderer->isRenderBlock() && !p.offset())
+ break;
+ InlineBox* box;
+ int ignoredCaretOffset;
+ p.getInlineBoxAndOffset(tempPosition.affinity(), box, ignoredCaretOffset);
+ if (box)
break;
startPosition = tempPosition;
}
ExceptionCode ec = 0;
ASSERT(range->startContainer(ec));
ASSERT(range->endContainer(ec));
- IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(range->startOffset(ec), DOWNSTREAM, &extraWidthToEndOfLine);
- ASSERT(!ec);
- IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(range->endOffset(ec), UPSTREAM);
- ASSERT(!ec);
-
+ InlineBox* startInlineBox;
+ int startCaretOffset;
+ range->startPosition().getInlineBoxAndOffset(DOWNSTREAM, startInlineBox, startCaretOffset);
+ IntRect startCaretRect = range->startContainer(ec)->renderer()->caretRect(startInlineBox, startCaretOffset, &extraWidthToEndOfLine);
+
+ InlineBox* endInlineBox;
+ int endCaretOffset;
+ range->endPosition().getInlineBoxAndOffset(UPSTREAM, endInlineBox, endCaretOffset);
+ IntRect endCaretRect = range->endContainer(ec)->renderer()->caretRect(endInlineBox, endCaretOffset);
+
if (startCaretRect.y() == endCaretRect.y()) {
// start and end are on the same line
return IntRect(min(startCaretRect.x(), endCaretRect.x()),
int InlineBox::caretMinOffset() const
{
- return 0;
+ return m_object->caretMinOffset();
}
int InlineBox::caretMaxOffset() const
{
- return 1;
+ return m_object->caretMaxOffset();
}
unsigned InlineBox::caretMaxRenderedOffset() const
#define InlineBox_h
#include "RenderObject.h" // needed for RenderObject::PaintInfo
+#include "TextDirection.h"
namespace WebCore {
, m_parent(0)
, m_firstLine(false)
, m_constructed(false)
+ , m_bidiEmbeddingLevel(0)
, m_dirty(false)
, m_extracted(false)
, m_includeLeftEdge(false)
, m_endsWithBreak(false)
, m_hasSelectedChildren(false)
, m_hasEllipsisBox(false)
- , m_reversed(false)
+ , m_dirOverride(false)
, m_treatAsText(true)
, m_determinedIfNextOnLineExists(false)
, m_determinedIfPrevOnLineExists(false)
, m_parent(parent)
, m_firstLine(firstLine)
, m_constructed(constructed)
+ , m_bidiEmbeddingLevel(0)
, m_dirty(dirty)
, m_extracted(extracted)
, m_includeLeftEdge(false)
, m_endsWithBreak(false)
, m_hasSelectedChildren(false)
, m_hasEllipsisBox(false)
- , m_reversed(false)
+ , m_dirOverride(false)
, m_treatAsText(true)
, m_determinedIfNextOnLineExists(false)
, m_determinedIfPrevOnLineExists(false)
virtual int caretMinOffset() const;
virtual int caretMaxOffset() const;
virtual unsigned caretMaxRenderedOffset() const;
-
+
+ unsigned char bidiLevel() const { return m_bidiEmbeddingLevel; }
+ void setBidiLevel(unsigned char level) { m_bidiEmbeddingLevel = level; }
+ TextDirection direction() const { return m_bidiEmbeddingLevel % 2 ? RTL : LTR; }
+ int caretLeftmostOffset() const { return direction() == LTR ? caretMinOffset() : caretMaxOffset(); }
+ int caretRightmostOffset() const { return direction() == LTR ? caretMaxOffset() : caretMinOffset(); }
+
virtual void clearTruncation() { }
bool isDirty() const { return m_dirty; }
bool m_firstLine : 1;
private:
bool m_constructed : 1;
+ unsigned char m_bidiEmbeddingLevel : 6;
protected:
bool m_dirty : 1;
bool m_extracted : 1;
// for InlineTextBox
public:
- bool m_reversed : 1;
bool m_dirOverride : 1;
bool m_treatAsText : 1; // Whether or not to treat a <br> as text for the purposes of line height.
protected:
int selHeight = selectionHeight();
const Font& f = textObj->style(m_firstLine)->font();
- IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
+ IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos));
if (r.x() > tx + m_x + m_width)
r.setWidth(0);
}
if (ellipsisX < m_x + m_width) {
- if (m_reversed)
+ if (direction() == RTL)
return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
foundBox = true;
int endPoint = m_len;
if (m_truncation != cNoTruncation)
endPoint = m_truncation;
- paintInfo.context->drawText(TextRun(textStr->characters() + m_start, endPoint, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
+ paintInfo.context->drawText(TextRun(textStr->characters() + m_start, endPoint, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()),
IntPoint(m_x + tx, m_y + ty + m_baseline));
} else {
int sPos, ePos;
if (paintSelectedTextSeparately) {
// paint only the text that is not selected
if (sPos >= ePos)
- paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
+ paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()),
IntPoint(m_x + tx, m_y + ty + m_baseline));
else {
if (sPos - 1 >= 0)
- paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
+ paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()),
IntPoint(m_x + tx, m_y + ty + m_baseline), 0, sPos);
if (ePos < m_start + m_len)
- paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
+ paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()),
IntPoint(m_x + tx, m_y + ty + m_baseline), ePos);
}
}
if (selectionTextShadow)
paintInfo.context->setShadow(IntSize(selectionTextShadow->x, selectionTextShadow->y),
selectionTextShadow->blur, selectionTextShadow->color);
- paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || styleToUse->visuallyOrdered()),
+ paintInfo.context->drawText(TextRun(textStr->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()),
IntPoint(m_x + tx, m_y + ty + m_baseline), sPos, ePos);
if (selectionTextShadow)
paintInfo.context->clearShadow();
int y = selectionTop();
int h = selectionHeight();
p->clip(IntRect(m_x + tx, y + ty, m_width, h));
- p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
+ p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
p->restore();
}
int y = selectionTop();
int h = selectionHeight();
- p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
+ p->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
IntPoint(m_x + tx, y + ty), h, c, sPos, ePos);
p->restore();
}
IntPoint startPoint = IntPoint(m_x + tx, y + ty);
int startPosition = max(marker.startOffset - m_start, (unsigned)0);
int endPosition = min(marker.endOffset - m_start, (unsigned)m_len);
- TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
+ TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered());
IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, startPoint, selectionHeight(), startPosition, endPosition));
object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect);
}
int sPos = max(marker.startOffset - m_start, (unsigned)0);
int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
- TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered());
+ TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered());
IntPoint startPoint = IntPoint(m_x + tx, y + ty);
// Always compute and store the rect associated with this marker
return 0;
RenderBlock *blockElement = object()->containingBlock();
- return m_reversed ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
+ return direction() == RTL ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
: xPos() - blockElement->borderLeft() - blockElement->paddingLeft();
}
RenderText* text = static_cast<RenderText*>(m_object);
RenderStyle *style = text->style(m_firstLine);
const Font* f = &style->font();
- return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride || style->visuallyOrdered()),
+ return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()),
_x - m_x, includePartialGlyphs);
}
RenderText* text = static_cast<RenderText*>(m_object);
const Font& f = text->style(m_firstLine)->font();
- int from = m_reversed ? offset - m_start : 0;
- int to = m_reversed ? m_len : offset - m_start;
+ int from = direction() == RTL ? offset - m_start : 0;
+ int to = direction() == RTL ? m_len : offset - m_start;
// FIXME: Do we need to add rightBearing here?
- return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, m_reversed, m_dirOverride),
+ return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride),
IntPoint(m_x, 0), 0, from, to)).right();
}
return VisiblePosition(element(), 0, DOWNSTREAM);
}
-InlineBox* RenderBR::inlineBox(int /*offset*/, EAffinity /*affinity*/)
-{
- return firstTextBox();
-}
-
} // namespace WebCore
virtual VisiblePosition positionForCoordinates(int x, int y);
- virtual InlineBox* inlineBox(int offset, EAffinity = UPSTREAM);
-
private:
mutable short m_lineHeight;
};
m_y = topValue + m_marginTop + containerBlock->borderTop();
}
-IntRect RenderBox::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
+IntRect RenderBox::caretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine)
{
// VisiblePositions at offsets inside containers either a) refer to the positions before/after
// those containers (tables and select elements) or b) refer to the position inside an empty block.
// FIXME: What about border and padding?
const int caretWidth = 1;
IntRect rect(xPos(), yPos(), caretWidth, m_height);
- if (offset)
+ TextDirection direction = box ? box->direction() : style()->direction();
+
+ if ((!caretOffset) ^ (direction == LTR))
rect.move(IntSize(m_width - caretWidth, 0));
- if (InlineBox* box = inlineBoxWrapper()) {
+
+ if (box) {
RootInlineBox* rootBox = box->root();
int top = rootBox->topOverflow();
rect.setY(top);
virtual RenderLayer* layer() const { return m_layer; }
- virtual IntRect caretRect(int offset, EAffinity = UPSTREAM, int* extraWidthToEndOfLine = 0);
+ virtual IntRect caretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight,
int tx, int ty, int width, int height, InlineFlowBox* = 0);
return left;
}
-IntRect RenderFlow::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
+IntRect RenderFlow::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
{
// Do the normal calculation in most cases.
if (firstChild() || style()->display() == INLINE)
- return RenderContainer::caretRect(offset, affinity, extraWidthToEndOfLine);
+ return RenderContainer::caretRect(inlineBox, caretOffset, extraWidthToEndOfLine);
// This is a special case:
// The element is not an inline element, and it's empty. So we have to
virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const;
virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const;
- virtual IntRect caretRect(int offset, EAffinity = UPSTREAM, int* extraWidthToEndOfLine = 0);
+ virtual IntRect caretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
virtual void addFocusRingRects(GraphicsContext*, int tx, int ty);
void paintOutlineForLine(GraphicsContext*, int tx, int ty, const IntRect& prevLine, const IntRect& thisLine, const IntRect& nextLine);
}
}
-IntRect RenderObject::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
+IntRect RenderObject::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
{
if (extraWidthToEndOfLine)
*extraWidthToEndOfLine = 0;
int RenderObject::caretMaxOffset() const
{
- return isReplaced() ? 1 : 0;
+ return isReplaced() ? (element() ? max(1U, element()->childNodeCount()) : 1) : 0;
}
unsigned RenderObject::caretMaxRenderedOffset() const
return current + 1;
}
-InlineBox* RenderObject::inlineBox(int offset, EAffinity affinity)
-{
- return inlineBoxWrapper();
-}
-
int RenderObject::maxTopMargin(bool positive) const
{
return positive ? max(0, marginTop()) : -min(0, marginTop());
virtual void setInlineBoxWrapper(InlineBox*);
virtual void deleteLineBoxWrapper();
- virtual InlineBox* inlineBox(int offset = 0, EAffinity = UPSTREAM);
-
// for discussion of lineHeight see CSS2 spec
virtual short lineHeight(bool firstLine, bool isRootLineBox = false) const;
// for the vertical-align property of inline elements
* @param extraWidthToEndOfLine optional out arg to give extra width to end of line -
* useful for character range rect computations
*/
- virtual IntRect caretRect(int offset, EAffinity = UPSTREAM, int* extraWidthToEndOfLine = 0);
+ virtual IntRect caretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
virtual int lowestPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; }
virtual int rightmostPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; }
return height() + marginTop() + marginBottom();
}
-int RenderReplaced::caretMinOffset() const
-{
- return 0;
-}
-
-// Returns 1 since a replaced element can have the caret positioned
-// at its beginning (0), or at its end (1).
-// NOTE: Yet, "select" elements can have any number of "option" elements
-// as children, so this "0 or 1" idea does not really hold up.
-int RenderReplaced::caretMaxOffset() const
-{
- return 1;
-}
-
unsigned RenderReplaced::caretMaxRenderedOffset() const
{
return 1;
virtual int overflowTop(bool includeInterior = true) const;
virtual IntRect overflowRect(bool includeInterior = true) const;
- virtual int caretMinOffset() const;
- virtual int caretMaxOffset() const;
virtual unsigned caretMaxRenderedOffset() const;
virtual VisiblePosition positionForCoordinates(int x, int y);
return new (renderArena()) SVGInlineTextBox(this);
}
-IntRect RenderSVGInlineText::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
+IntRect RenderSVGInlineText::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
{
// SVG doesn't have any editable content where a caret rect would be needed
return IntRect();
for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) {
if (box->svgCharacterHitsPosition(x + object->xPos(), y + object->yPos(), offset)) {
// If we're not at the end/start of the box, stop looking for other selected boxes.
- if (!box->m_reversed) {
+ if (box->direction() == LTR) {
if (offset <= (int) box->end() + 1)
break;
} else {
virtual bool isSVGText() const { return true; }
virtual InlineTextBox* createInlineTextBox();
- virtual IntRect caretRect(int offset, EAffinity, int* extraWidthToEndOfLine = 0);
+ virtual IntRect caretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
virtual VisiblePosition positionForCoordinates(int x, int y);
private:
return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->m_start + lastBoxAbove->m_len : 0, DOWNSTREAM);
}
-static inline bool atLineWrap(InlineTextBox* box, int offset)
+IntRect RenderText::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine)
{
- return box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len;
-}
-
-IntRect RenderText::caretRect(int offset, EAffinity affinity, int* extraWidthToEndOfLine)
-{
- if (!firstTextBox() || !textLength())
+ if (!inlineBox)
return IntRect();
- // Find the text box for the given offset
- InlineTextBox* box = 0;
- for (box = firstTextBox(); box; box = box->nextTextBox()) {
- if (box->containsCaretOffset(offset)) {
- // Check if downstream affinity would make us move to the next line.
- if (atLineWrap(box, offset) && affinity == DOWNSTREAM) {
- // Use the next text box
- box = box->nextTextBox();
- offset = box->m_start;
- } else {
- InlineTextBox* prevBox = box->prevTextBox();
- if (offset == box->m_start && affinity == UPSTREAM && prevBox && !box->prevOnLine()) {
- box = prevBox;
- offset = box->m_start + box->m_len;
- }
- }
- break;
- }
- }
-
- if (!box)
+ ASSERT(inlineBox->isInlineTextBox());
+ if (!inlineBox->isInlineTextBox())
return IntRect();
+ InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox);
+
int height = box->root()->bottomOverflow() - box->root()->topOverflow();
int top = box->root()->topOverflow();
- int left = box->positionForOffset(offset);
+ int left = box->positionForOffset(caretOffset);
int rootLeft = box->root()->xPos();
// FIXME: should we use the width of the root inline box or the
RenderBlock* cb = containingBlock();
if (style()->autoWrap()) {
int availableWidth = cb->lineWidth(top);
- if (!box->m_reversed)
+ if (box->direction() == LTR)
left = min(left, absx + rootLeft + availableWidth - 1);
else
left = max(left, absx + rootLeft);
return;
}
- m_containsReversedText |= s->m_reversed;
+ m_containsReversedText |= s->direction() == RTL;
}
unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const
return result;
}
-InlineBox* RenderText::inlineBox(int offset, EAffinity affinity)
-{
- for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
- if (box->containsCaretOffset(offset)) {
- if (atLineWrap(box, offset) && affinity == DOWNSTREAM)
- return box->nextTextBox();
- return box;
- }
- if (offset < box->m_start)
- // The offset we're looking for is before this node
- // this means the offset must be in content that is
- // not rendered.
- return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
- }
-
- return 0;
-}
-
#ifndef NDEBUG
void RenderText::checkConsistency() const
virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); }
virtual void setSelectionState(SelectionState s);
virtual IntRect selectionRect(bool clipToVisibleContent = true);
- virtual IntRect caretRect(int offset, EAffinity, int* extraWidthToEndOfLine = 0);
+ virtual IntRect caretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0);
virtual int marginLeft() const { return style()->marginLeft().calcMinValue(0); }
virtual int marginRight() const { return style()->marginRight().calcMinValue(0); }
InlineTextBox* firstTextBox() const { return m_firstTextBox; }
InlineTextBox* lastTextBox() const { return m_lastTextBox; }
- virtual InlineBox* inlineBox(int offset, EAffinity = UPSTREAM);
-
virtual int caretMinOffset() const;
virtual int caretMaxOffset() const;
virtual unsigned caretMaxRenderedOffset() const;
if (!renderer)
return "";
- InlineBox* box = renderer->inlineBox(0, DOWNSTREAM);
+ InlineBox* box = renderer->isText() ? static_cast<RenderText*>(renderer)->firstTextBox() : renderer->inlineBoxWrapper();
if (!box)
return "";
static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
{
ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width;
- if (run.m_reversed || run.m_dirOverride) {
- ts << (run.m_reversed ? " RTL" : " LTR");
+ if (run.direction() == RTL || run.m_dirOverride) {
+ ts << (run.direction() == RTL ? " RTL" : " LTR");
if (run.m_dirOverride)
ts << " override";
}
// FIXME: account for multi-character glyphs
int charsConsumed;
String glyphName;
- if (!m_reversed)
+ if (direction() == LTR)
glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName);
else
glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed, glyphName);
// Take RTL text into account and pick right glyph width/height.
// NOTE: This offset has to be corrected _after_ calling calculateGlyphBoundaries
- if (textBox->m_reversed)
+ if (textBox->direction() == RTL)
newOffset = textBox->start() + textBox->end() - newOffset;
// Calculate distances relative to the glyph mid-point. I hope this is accurate enough.
RenderStyle* style = textObject()->style(m_firstLine);
FloatRect glyphRect = calculateGlyphBoundaries(style, offset, charAtPos);
- if (m_reversed)
+ if (direction() == RTL)
offset++;
// FIXME: todo list
// Check whether x position hits the current character
if (x < charAtPos.x) {
- if (offset > 0 && !m_reversed)
+ if (offset > 0 && direction() == LTR)
return true;
- else if (offset < (int) end() && m_reversed)
+ else if (offset < (int) end() && direction() == RTL)
return true;
return false;
// Snap to character at half of it's advance
if (x >= charAtPos.x + glyphRect.width() / 2.0)
- offset += m_reversed ? -1 : 1;
+ offset += direction() == RTL ? -1 : 1;
return true;
}
else
ts << " width " << cummulatedWidthOfInlineBoxCharacterRange(range);
- if (textBox->m_reversed || textBox->m_dirOverride) {
- ts << (textBox->m_reversed ? " RTL" : " LTR");
+ if (textBox->direction() == RTL || textBox->m_dirOverride) {
+ ts << (textBox->direction() == RTL ? " RTL" : " LTR");
if (textBox->m_dirOverride)
ts << " override";
ASSERT(textBox);
ASSERT(style);
- TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->m_reversed, textBox->m_dirOverride || style->visuallyOrdered());
+ TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered());
#if ENABLE(SVG_FONTS)
run.setReferencingRenderObject(textBox->textObject()->parent());
SVGChar& lastCharacter = *(itSearch - 1);
SVGChar& currentCharacter = *itSearch;
- int offset = box->m_reversed ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
+ int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1;
// FIXME: does this need to change to handle multichar glyphs?
int charsConsumed = 1;
String unicodeStr;
String glyphName;
- if (textBox->m_reversed) {
+ if (textBox->direction() == RTL) {
glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName);
glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable);
unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed);
// Take letter & word spacing and kerning into account
float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer());
- const UChar* currentCharacter = text->characters() + (textBox->m_reversed ? textBox->end() - i : textBox->start() + i);
+ const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i);
const UChar* lastCharacter = 0;
- if (textBox->m_reversed) {
+ if (textBox->direction() == RTL) {
if (i < textBox->end())
lastCharacter = text->characters() + textBox->end() - i + 1;
} else {
bool isOnlyRun = (runCount == 1);
if (runCount == 2 && !r->m_object->isListMarker())
isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker();
- r->m_box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun);
- if (r->m_box) {
+ InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun);
+ r->m_box = box;
+
+ if (box) {
// If we have no parent box yet, or if the run is not simply a sibling,
// then we need to construct inline boxes as necessary to properly enclose the
// run's inline box.
parentBox = createLineBoxes(r->m_object->parent());
// Append the inline box to this line.
- parentBox->addToLine(r->m_box);
-
- if (r->m_box->isInlineTextBox()) {
- InlineTextBox* text = static_cast<InlineTextBox*>(r->m_box);
+ parentBox->addToLine(box);
+
+ bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
+ box->setBidiLevel(visuallyOrdered ? 0 : r->level());
+
+ if (box->isInlineTextBox()) {
+ InlineTextBox* text = static_cast<InlineTextBox*>(box);
text->setStart(r->m_start);
text->setLen(r->m_stop - r->m_start);
- bool visuallyOrdered = r->m_object->style()->visuallyOrdered();
- text->m_reversed = r->reversed(visuallyOrdered);
text->m_dirOverride = r->dirOverride(visuallyOrdered);
}
}
unsigned int newOffset = textBox->start() + (it - start) + startOffset;
// Take RTL text into account and pick right glyph width/height.
- if (textBox->m_reversed)
+ if (textBox->direction() == RTL)
newOffset = textBox->start() + textBox->end() - newOffset;
// FIXME: does this handle multichar glyphs ok? not sure
unsigned int newOffset = textBox->start() + (it - start) + startOffset;
// Take RTL text into account and pick right glyph width/height.
- if (textBox->m_reversed)
+ if (textBox->direction() == RTL)
newOffset = textBox->start() + textBox->end() - newOffset;
int charsConsumed;
+2008-04-24 Dan Bernstein <mitz@apple.com>
+
+ Reviewed by Darin Adler.
+
+ - preparation for https://bugs.webkit.org/show_bug.cgi?id=3729
+ <rdar://problem/4036353> REGRESSION: arrow keys move insertion bar backwards in RTL text
+
+ * WebView/WebFrame.mm:
+ (-[WebFrame _caretRectAtNode:offset:affinity:]): Changed to use
+ VisiblePosition::caretRect() instead of the RenderObject method which
+ was removed.
+
2008-04-24 Brady Eidson <beidson@apple.com>
Reviewed by Darin
- (NSRect)_caretRectAtNode:(DOMNode *)node offset:(int)offset affinity:(NSSelectionAffinity)affinity
{
- return [node _node]->renderer()->caretRect(offset, static_cast<EAffinity>(affinity));
+ VisiblePosition visiblePosition([node _node], offset, static_cast<EAffinity>(affinity));
+ return visiblePosition.caretRect();
}
- (NSRect)_firstRectForDOMRange:(DOMRange *)range