| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "core/editing/LayoutPosition.h" |
| 6 |
| 7 #include "core/dom/DocumentLifecycle.h" |
| 8 #include "core/dom/NodeTraversal.h" |
| 9 #include "core/editing/LayoutTreeTraversal.h" |
| 10 #include "core/editing/VisibleUnits.h" |
| 11 #include "core/frame/FrameView.h" |
| 12 #include "core/html/HTMLImageElement.h" |
| 13 #include "core/layout/LayoutBR.h" |
| 14 #include "core/layout/LayoutBlockFlow.h" |
| 15 #include "core/layout/LayoutInline.h" |
| 16 #include "core/layout/LayoutObject.h" |
| 17 #include "core/layout/LayoutText.h" |
| 18 #include "core/layout/LayoutTextFragment.h" |
| 19 #include "core/layout/api/LineLayoutAPIShim.h" |
| 20 #include "core/layout/api/LineLayoutItem.h" |
| 21 #include "core/layout/line/InlineTextBox.h" |
| 22 #include "wtf/Assertions.h" |
| 23 #include <ostream> // NOLINT |
| 24 |
| 25 namespace blink { |
| 26 |
| 27 namespace { |
| 28 InlineBoxPosition findInlineBoxPosition(const Text& textNode, int offsetInNo
de) |
| 29 { |
| 30 DCHECK_GE(offsetInNode, 0); |
| 31 LayoutObject* const layoutObject = associatedLayoutObjectOf(textNode, of
fsetInNode); |
| 32 if (!layoutObject) |
| 33 return InlineBoxPosition(); |
| 34 LayoutText* const layoutText = toLayoutText(layoutObject); |
| 35 CHECK_GE(static_cast<unsigned>(offsetInNode), layoutText->textStartOffse
t()); |
| 36 InlineTextBox* afterBox = nullptr; |
| 37 InlineTextBox* beforeBox = nullptr; |
| 38 const unsigned offsetInText = static_cast<unsigned>(offsetInNode - layou
tText->textStartOffset()); |
| 39 for (InlineTextBox* box = layoutText->firstTextBox(); box; box = box->ne
xtTextBox()) { |
| 40 if (offsetInText >= box->start() && offsetInText <= box->end()) |
| 41 return InlineBoxPosition(box, offsetInText); |
| 42 if (box->start() > offsetInText) { |
| 43 if (!afterBox || afterBox->start() > box->start()) |
| 44 afterBox = box; |
| 45 continue; |
| 46 } |
| 47 if (box->end() < offsetInText) { |
| 48 if (!beforeBox || beforeBox->end() < box->end()) |
| 49 beforeBox = box; |
| 50 continue; |
| 51 } |
| 52 } |
| 53 if (beforeBox) { |
| 54 DCHECK_LT(beforeBox->end(), offsetInText); |
| 55 return InlineBoxPosition(beforeBox, beforeBox->end() + 1); |
| 56 } |
| 57 if (afterBox) { |
| 58 DCHECK_GT(afterBox->start(), offsetInText); |
| 59 return InlineBoxPosition(afterBox, afterBox->start()); |
| 60 } |
| 61 return InlineBoxPosition(); |
| 62 } |
| 63 |
| 64 // Note: Since |LayoutBR| is derived from |LayoutText|, we need to |
| 65 // use associated node instead of |LayoutObject::isText()|. |
| 66 // Note: |LayoutObject::node()| returns null for a layout object for |
| 67 // first-letter fragment. |
| 68 // |
| 69 // Following nodes are associated to anonymous LayoutText object: |
| 70 // - ::before/::after pseudo elements |
| 71 // - OPTION elements when SELECT[@size=1] |
| 72 bool shouldUseOffsetInAnchor(const LayoutObject& layoutObject) |
| 73 { |
| 74 if (!layoutObject.isText()) |
| 75 return false; |
| 76 const LayoutText& layoutText = toLayoutText(layoutObject); |
| 77 if (!layoutText.firstTextBox()) |
| 78 return false; |
| 79 Node* const node = layoutText.node(); |
| 80 if (node) |
| 81 return node->isTextNode(); |
| 82 Node* parentNode = layoutObject.parent()->node(); |
| 83 return parentNode && parentNode->isFirstLetterPseudoElement(); |
| 84 } |
| 85 |
| 86 bool shouldUseOffsetInAnchor(const InlineBox& inlineBox) |
| 87 { |
| 88 Node* const node = inlineBox.getLineLayoutItem().node(); |
| 89 return node && node->isTextNode(); |
| 90 } |
| 91 |
| 92 InlineBox* inlineBoxOf(const LayoutObject& layoutObject) |
| 93 { |
| 94 if (layoutObject.isBR()) |
| 95 return toLayoutBR(layoutObject).firstTextBox(); |
| 96 DCHECK(!shouldUseOffsetInAnchor(layoutObject)) |
| 97 << "A position for LayoutText should be constructed OffsetInAnchor:
" << layoutObject; |
| 98 if (!layoutObject.isBox()) |
| 99 return nullptr; |
| 100 return toLayoutBox(layoutObject).inlineBoxWrapper(); |
| 101 } |
| 102 } // namespace |
| 103 |
| 104 LayoutPosition::LayoutPosition(PositionAnchorType anchorType, LayoutObject* layo
utObject) |
| 105 : m_anchorType(anchorType) |
| 106 , m_layoutObject(layoutObject) |
| 107 , m_inlineBox(inlineBoxOf(*layoutObject)) |
| 108 { |
| 109 DCHECK(m_anchorType == PositionAnchorType::AfterAnchor || m_anchorType == Po
sitionAnchorType::BeforeAnchor || m_anchorType == PositionAnchorType::BeforeChil
dren) << m_anchorType; |
| 110 DCHECK(m_layoutObject); |
| 111 DCHECK(!shouldUseOffsetInAnchor(*m_layoutObject)) << m_layoutObject; |
| 112 #if ENABLE(ASSERT) |
| 113 m_domTreeVersion = document().domTreeVersion(); |
| 114 m_styleVersion = document().styleVersion(); |
| 115 #endif |
| 116 } |
| 117 |
| 118 LayoutPosition::LayoutPosition(const InlineBoxPosition& boxPosition) |
| 119 : m_anchorType(PositionAnchorType::OffsetInAnchor) |
| 120 , m_inlineBox(boxPosition.inlineBox) |
| 121 , m_layoutObject(layoutObjectOf(*boxPosition.inlineBox)) |
| 122 , m_offsetInBox(boxPosition.offsetInBox) |
| 123 { |
| 124 DCHECK(m_inlineBox); |
| 125 DCHECK(m_inlineBox->isInlineTextBox()); |
| 126 DCHECK(m_layoutObject->isText()); |
| 127 DCHECK(shouldUseOffsetInAnchor(*m_layoutObject)) << m_layoutObject; |
| 128 DCHECK_GE(m_offsetInBox, static_cast<int>(toInlineTextBox(m_inlineBox)->star
t())); |
| 129 DCHECK_LE(m_offsetInBox, static_cast<int>(toInlineTextBox(m_inlineBox)->end(
) + 1)); |
| 130 #if ENABLE(ASSERT) |
| 131 m_domTreeVersion = document().domTreeVersion(); |
| 132 m_styleVersion = document().styleVersion(); |
| 133 #endif |
| 134 } |
| 135 |
| 136 LayoutPosition::LayoutPosition(const LayoutPosition& other) |
| 137 : m_anchorType(other.m_anchorType) |
| 138 , m_inlineBox(other.m_inlineBox) |
| 139 , m_layoutObject(other.m_layoutObject) |
| 140 , m_offsetInBox(other.m_offsetInBox) |
| 141 { |
| 142 #if ENABLE(ASSERT) |
| 143 m_domTreeVersion = other.m_domTreeVersion; |
| 144 m_styleVersion = other.m_styleVersion; |
| 145 #endif |
| 146 } |
| 147 |
| 148 LayoutPosition::LayoutPosition() |
| 149 : m_anchorType(PositionAnchorType::BeforeAnchor) |
| 150 { |
| 151 } |
| 152 |
| 153 LayoutPosition::~LayoutPosition() |
| 154 { |
| 155 } |
| 156 |
| 157 bool LayoutPosition::operator==(const LayoutPosition& other) const |
| 158 { |
| 159 DCHECK(isValid()) << *this; |
| 160 DCHECK(other.isValid()) << other; |
| 161 if (this == &other) |
| 162 return true; |
| 163 return m_anchorType == other.m_anchorType && m_layoutObject == other.m_layou
tObject && m_inlineBox == other.m_inlineBox && m_offsetInBox == other.m_offsetIn
Box; |
| 164 } |
| 165 |
| 166 bool LayoutPosition::operator!=(const LayoutPosition& other) const |
| 167 { |
| 168 return !operator==(other); |
| 169 } |
| 170 |
| 171 // Returns false when |m_layoutObject| is anonymous or |m_layoutObject| is |
| 172 // associated to pseudo-element, e.g. ::before, ::after, ::first-letter, etc. |
| 173 bool LayoutPosition::canBePosition() const |
| 174 { |
| 175 DCHECK(isValid()) << *this; |
| 176 if (isNull()) |
| 177 return true; |
| 178 const Node* const node = m_layoutObject->nonPseudoNode(); |
| 179 if (!node) |
| 180 return false; |
| 181 if (node->isDocumentNode()) { |
| 182 // We can't construct a position before/after document. |
| 183 return false; |
| 184 } |
| 185 if (!node->isInShadowTree()) |
| 186 return true; |
| 187 Element* shadowHost = node->shadowHost(); |
| 188 if (node == shadowHost) |
| 189 return true; |
| 190 if (!isHTMLImageElement(shadowHost)) |
| 191 return true; |
| 192 // We can't place caret fallback content of IMG element. |
| 193 return !shadowHost->userAgentShadowRoot(); |
| 194 } |
| 195 |
| 196 bool LayoutPosition::canPlaceCaret() const |
| 197 { |
| 198 if (!canPlaceCaretInternal()) |
| 199 return false; |
| 200 DCHECK(!computeCaretQuad().isEmpty()) |
| 201 << "canPlaceCaret() says true, but caret quad is empty for " << *this; |
| 202 return true; |
| 203 } |
| 204 |
| 205 bool LayoutPosition::canPlaceCaretInternal() const |
| 206 { |
| 207 if (isNull() || !canBePosition()) |
| 208 return false; |
| 209 const Node* node = m_layoutObject->nonPseudoNode(); |
| 210 if (!node) { |
| 211 // We can't place a caret in pseudo element and auto created layout |
| 212 // object. |
| 213 return false; |
| 214 } |
| 215 if (node == node->document().documentElement()) { |
| 216 // We can't place a caret before/after document element. |
| 217 return isBeforeChildren(); |
| 218 } |
| 219 if (m_layoutObject->style()->visibility() != VISIBLE) |
| 220 return false; |
| 221 if (m_layoutObject->isBR()) { |
| 222 // Since, |LayoutObject::localCaretRect()| doesn't support after BR. |
| 223 // We don't allow to place caret after BR. |
| 224 return isBeforeAnchor() && toLayoutText(m_layoutObject)->hasTextBoxes(); |
| 225 } |
| 226 if (m_layoutObject->isText()) { |
| 227 if (!m_inlineBox) { |
| 228 DCHECK(isBeforeAnchor() || isAfterAnchor()) << this; |
| 229 // We can't place caret before/after collapsed whitespace. |
| 230 return false; |
| 231 } |
| 232 DCHECK(isOffsetInAnchor()) << this; |
| 233 return !U16_IS_TRAIL(codeUnitAfter()); |
| 234 } |
| 235 if (m_layoutObject->isSVG()) { |
| 236 // We don't consider SVG elements are contenteditable except for |
| 237 // associated |layoutObject| returns |isText()| true, |
| 238 // e.g. |LayoutSVGInlineText|. |
| 239 return false; |
| 240 } |
| 241 // TODO(yosin): We should have |LayoutObject::canPlaceCaretBefore()|. |
| 242 if (!m_layoutObject->canHaveChildren()) { |
| 243 // We can place caret before/after replaced elements, e.g. CANVAS, |
| 244 // IMG, etc. |
| 245 // Note: when IMG element has alt/title and no image, IMG element |
| 246 // yields |LayoutBlockFlow| instead of |LayoutImage|. |
| 247 return isBeforeAnchor() || isAfterAnchor(); |
| 248 } |
| 249 if (m_layoutObject->isRuby()) { |
| 250 // We can't place a caret before/after RUBY element. |isRuby| checks |
| 251 // must be done before |isLayoutInline()| since |LayoutRubyInline| is |
| 252 // derived from |LayoutInline|. |
| 253 return isBeforeChildren(); |
| 254 } |
| 255 // TODO(yosin): |LayoutProgress::canHaveChildren()| should return false. |
| 256 if (m_layoutObject->isProgress()) |
| 257 return isBeforeAnchor() || isAfterAnchor(); |
| 258 if (m_layoutObject->isLayoutButton() || m_layoutObject->isTable() || m_layou
tObject->isTextControl() || m_layoutObject->isMenuList() || m_layoutObject->isMe
dia()) |
| 259 return isBeforeAnchor() || isAfterAnchor(); |
| 260 // TODO(yosin): Should we use |LayoutBox| to cover both |LayoutBlockFLow| |
| 261 // and |LayoutInline|? |
| 262 if (m_layoutObject->isLayoutBlock()) { |
| 263 if (!isBeforeChildren()) |
| 264 return false; |
| 265 #if 0 |
| 266 // We can place a caret in block having no children and the block |
| 267 // is an editing host. |
| 268 if (m_layoutObject->style()->userModify() == READ_ONLY) |
| 269 return false; |
| 270 return m_layoutObject->parent()->style()->userModify() == READ_ONLY; |
| 271 #else |
| 272 return !m_inlineBox; |
| 273 #endif |
| 274 } |
| 275 if (m_layoutObject->isLayoutInline()) { |
| 276 DCHECK(!m_inlineBox) << "When do we have inline box for LayoutInline?"; |
| 277 if (isBeforeAnchor()) { |
| 278 if (m_layoutObject->style()->userModify() == READ_ONLY) |
| 279 return false; |
| 280 LayoutObject* parent = m_layoutObject->parent(); |
| 281 if (parent->style()->userModify() == READ_ONLY) |
| 282 return false; |
| 283 if (parent->parent()->style()->userModify() != READ_ONLY) |
| 284 return false; |
| 285 // We can place a caret before first child of editable root. |
| 286 // e.g. <span contenteditable>|<b></b><i></i></span> |
| 287 if (LayoutTreeTraversal::firstChild(*parent) != m_layoutObject) |
| 288 return false; |
| 289 return !computeCaretQuad().isEmpty(); |
| 290 } |
| 291 if (isBeforeChildren()) { |
| 292 if (m_layoutObject->style()->userModify() == READ_ONLY) |
| 293 return false; |
| 294 // We can place a caret for root editable empty inline element, |
| 295 // e.g. <span contenteditable></span> |
| 296 if (m_layoutObject->parent()->style()->userModify() != READ_ONLY) |
| 297 return false; |
| 298 return !computeCaretQuad().isEmpty(); |
| 299 } |
| 300 return false; |
| 301 } |
| 302 return false; |
| 303 } |
| 304 |
| 305 UChar LayoutPosition::codeUnitAfter() const |
| 306 { |
| 307 DCHECK(isOffsetInAnchor()) << *this; |
| 308 return toInlineTextBox(m_inlineBox)->text()[m_offsetInBox]; |
| 309 } |
| 310 |
| 311 FloatQuad LayoutPosition::computeCaretQuad() const |
| 312 { |
| 313 if (m_hasCaretQuad) |
| 314 return m_caretQuad; |
| 315 m_hasCaretQuad = true; |
| 316 if (isNull()) |
| 317 return FloatQuad(); |
| 318 const InlineBoxPosition& inlineBoxPosition = computeInlineBoxPosition(); |
| 319 // TODO(yosin): We should make |LayoutObject::localCaretRect()| to take |
| 320 // |const InlineBox*|. |
| 321 const LayoutRect& localCaretRect = m_layoutObject->localCaretRect(inlineBoxP
osition.inlineBox, inlineBoxPosition.offsetInBox); |
| 322 if (localCaretRect.isEmpty()) |
| 323 return FloatQuad(); |
| 324 m_caretQuad = m_layoutObject->localToAbsoluteQuad(FloatRect(localCaretRect))
; |
| 325 return m_caretQuad; |
| 326 } |
| 327 |
| 328 InlineBoxPosition LayoutPosition::computeInlineBoxPosition() const |
| 329 { |
| 330 if (m_inlineBox) |
| 331 return InlineBoxPosition(m_inlineBox, m_offsetInBox); |
| 332 DCHECK_EQ(m_offsetInBox, 0); |
| 333 if (m_layoutObject->isBox()) { |
| 334 InlineBox* const inlineBox = toLayoutBox(m_layoutObject)->inlineBoxWrapp
er(); |
| 335 if (isAfterAnchor()) |
| 336 return InlineBoxPosition(inlineBox, 1); |
| 337 return InlineBoxPosition(inlineBox, 0); |
| 338 } |
| 339 |
| 340 if (m_layoutObject->isText() && toLayoutText(m_layoutObject)->hasTextBoxes()
) { |
| 341 if (isAfterAnchor()) { |
| 342 InlineTextBox * const inlineBox = toLayoutText(m_layoutObject)->last
TextBox(); |
| 343 return InlineBoxPosition(inlineBox, inlineBox->end()); |
| 344 } |
| 345 InlineTextBox * const inlineBox = toLayoutText(m_layoutObject)->firstTex
tBox(); |
| 346 return InlineBoxPosition(inlineBox, inlineBox->start()); |
| 347 } |
| 348 return InlineBoxPosition(); |
| 349 } |
| 350 |
| 351 const Document& LayoutPosition::document() const |
| 352 { |
| 353 DCHECK(isNotNull()); |
| 354 return m_layoutObject->document(); |
| 355 } |
| 356 |
| 357 bool LayoutPosition::isCaretEquivalent(const LayoutPosition& other) const |
| 358 { |
| 359 if (*this == other) |
| 360 return true; |
| 361 if (!canPlaceCaret()) |
| 362 return !other.canPlaceCaret(); |
| 363 if (!other.canPlaceCaret()) |
| 364 return false; |
| 365 if (m_layoutObject == other.m_layoutObject && m_inlineBox == other.m_inlineB
ox && m_offsetInBox == other.m_offsetInBox) { |
| 366 // TODO(yosin): Is BeforeChildren is different from BeforeAnchor and |
| 367 // AfterAnchor? |
| 368 // Example: |
| 369 // |<span></span> vs <span>|</span> |
| 370 return computeCaretQuad().isEmpty(); |
| 371 } |
| 372 |
| 373 if (isAfterAnchor() && m_inlineBox) { |
| 374 // In case of a position at end of line and a position at start of |
| 375 // next line. |
| 376 if (m_inlineBox->nextOnLine() != other.m_inlineBox) |
| 377 return false; |
| 378 if (other.isBeforeAnchor()) |
| 379 return true; |
| 380 if (other.isOffsetInAnchor()) |
| 381 return static_cast<unsigned>(other.m_offsetInBox) == toInlineTextBox
(other.m_inlineBox)->start(); |
| 382 return false; |
| 383 } |
| 384 |
| 385 if (other.isAfterAnchor() && other.m_inlineBox) |
| 386 return !isAfterAnchor() && other.isCaretEquivalent(*this); |
| 387 |
| 388 if (isOffsetInAnchor() && other.isOffsetInAnchor()) { |
| 389 // Note: for "<span>A</span>B", carets for position after "A" and |
| 390 // position before "B" are same.. |
| 391 if (m_inlineBox->parent() != other.m_inlineBox->parent()) { |
| 392 // |this| and |other| are in different lines. |
| 393 return false; |
| 394 } |
| 395 } |
| 396 |
| 397 #if 0 |
| 398 if (m_layoutObject->isLayoutBlock() || other.m_layoutObject->isLayoutBlock()
) { |
| 399 if (LayoutTreeTraversal::parent(*m_layoutObject) == other.m_layoutObject
|| LayoutTreeTraversal::parent(*other.m_layoutObject) == m_layoutObject) { |
| 400 // We consider before anchor and inside anchor are different. |
| 401 return false; |
| 402 } |
| 403 } |
| 404 #endif |
| 405 // TODO(yosin): Once we finish debugging of |isCaretEquivalent()|, we |
| 406 // should not use local variables for caret quad. |
| 407 const auto& thisQuad = computeCaretQuad(); |
| 408 const auto& otherQuad = other.computeCaretQuad(); |
| 409 return thisQuad == otherQuad; |
| 410 } |
| 411 |
| 412 // See |primaryDictionOf()| in "EditingUtilities.cpp". |
| 413 bool LayoutPosition::isLeftToRightDirection() const |
| 414 { |
| 415 DCHECK(isNotNull()); |
| 416 if (m_inlineBox) |
| 417 return m_inlineBox->isLeftToRightDirection(); |
| 418 for (const LayoutObject* runner = m_layoutObject; runner; runner = LayoutTre
eTraversal::parent(*runner)) { |
| 419 if (runner->isLayoutBlockFlow()) |
| 420 return runner->style()->direction() == LTR; |
| 421 } |
| 422 NOTREACHED(); |
| 423 return false; |
| 424 } |
| 425 |
| 426 bool LayoutPosition::isValid() const |
| 427 { |
| 428 #if ENABLE(ASSERT) |
| 429 if (isNull()) |
| 430 return true; |
| 431 if (!document().isActive()) |
| 432 return false; |
| 433 if (document().view()->needsLayout()) |
| 434 return false; |
| 435 // TODO(yosin): We should introduce const version of |Document::lifecycle()| |
| 436 if (const_cast<Document&>(document()).lifecycle().state() < DocumentLifecycl
e::LayoutClean) |
| 437 return false; |
| 438 return document().domTreeVersion() == m_domTreeVersion && document().styleVe
rsion() == m_styleVersion; |
| 439 #else |
| 440 return true; |
| 441 #endif |
| 442 } |
| 443 |
| 444 template <typename Strategy> |
| 445 PositionTemplate<Strategy> LayoutPosition::toPositionAlgorithm(const PositionTem
plate<Strategy>&) const |
| 446 { |
| 447 DCHECK(canBePosition()) << *this; |
| 448 if (isNull()) |
| 449 return PositionTemplate<Strategy>(); |
| 450 Node* const node = m_layoutObject->nonPseudoNode(); |
| 451 if (!node) |
| 452 return PositionTemplate<Strategy>(); |
| 453 if (isOffsetInAnchor()) { |
| 454 // TODO(yosin): |LayoputText| contains more or less characters than |Tex
t| |
| 455 // node due by CSS text-transform, e.g. transform German Eszet to upper |
| 456 // case. http://crbug.com/27616 |
| 457 const int offsetInNode = toLayoutText(m_layoutObject)->textStartOffset()
+ m_offsetInBox; |
| 458 return PositionTemplate<Strategy>(node, std::min(offsetInNode, static_ca
st<int>(toText(node)->length()))); |
| 459 } |
| 460 if (isBeforeAnchor()) |
| 461 return PositionTemplate<Strategy>::beforeNode(node); |
| 462 if (isBeforeChildren()) { |
| 463 // TODO(yosin): Once we update layout tests, we should use |
| 464 // |firstPositionInNode() to have a BeforeChildren position. |
| 465 // return PositionTemplate<Strategy>(node, 0); |
| 466 return PositionTemplate<Strategy>::firstPositionInNode(node); |
| 467 } |
| 468 DCHECK(isAfterAnchor()) << *this; |
| 469 return PositionTemplate<Strategy>::afterNode(node); |
| 470 } |
| 471 |
| 472 Position LayoutPosition::toPosition(const Position& sample) const |
| 473 { |
| 474 return toPositionAlgorithm<EditingStrategy>(sample); |
| 475 } |
| 476 |
| 477 PositionInFlatTree LayoutPosition::toPosition(const PositionInFlatTree& sample)
const |
| 478 { |
| 479 return toPositionAlgorithm<EditingInFlatTreeStrategy>(sample); |
| 480 } |
| 481 |
| 482 // static |
| 483 LayoutPosition LayoutPosition::afterAnchorOf(InlineBox* inlineBox) |
| 484 { |
| 485 if (shouldUseOffsetInAnchor(*inlineBox)) |
| 486 return LayoutPosition(InlineBoxPosition(inlineBox, toInlineTextBox(inlin
eBox)->end() + 1)); |
| 487 return afterAnchorOf(layoutObjectOf(*inlineBox)); |
| 488 } |
| 489 |
| 490 // static |
| 491 LayoutPosition LayoutPosition::afterAnchorOf(LayoutObject* layoutObject) |
| 492 { |
| 493 if (shouldUseOffsetInAnchor(*layoutObject)) { |
| 494 InlineTextBox* const textBox = toLayoutText(layoutObject)->lastTextBox()
; |
| 495 return LayoutPosition(InlineBoxPosition(textBox, textBox->end() + 1)); |
| 496 } |
| 497 return LayoutPosition(PositionAnchorType::AfterAnchor, layoutObject); |
| 498 } |
| 499 |
| 500 // static |
| 501 LayoutPosition LayoutPosition::beforeAnchorOf(InlineBox* inlineBox) |
| 502 { |
| 503 if (shouldUseOffsetInAnchor(*inlineBox)) |
| 504 return LayoutPosition(InlineBoxPosition(inlineBox, toInlineTextBox(inlin
eBox)->start())); |
| 505 return LayoutPosition(PositionAnchorType::BeforeAnchor, layoutObjectOf(*inli
neBox)); |
| 506 } |
| 507 |
| 508 // static |
| 509 LayoutPosition LayoutPosition::beforeAnchorOf(LayoutObject* layoutObject) |
| 510 { |
| 511 if (shouldUseOffsetInAnchor(*layoutObject)) { |
| 512 InlineTextBox* const textBox = toLayoutText(layoutObject)->firstTextBox(
); |
| 513 return LayoutPosition(InlineBoxPosition(textBox, textBox->start())); |
| 514 } |
| 515 return LayoutPosition(PositionAnchorType::BeforeAnchor, layoutObject); |
| 516 } |
| 517 |
| 518 // static |
| 519 LayoutPosition LayoutPosition::beforeChildrenOf(LayoutObject* layoutObject) |
| 520 { |
| 521 DCHECK(layoutObject->canHaveChildren()) << layoutObject; |
| 522 return LayoutPosition(PositionAnchorType::BeforeChildren, layoutObject); |
| 523 } |
| 524 |
| 525 // Returns a |LayoutPosition| for |position| for |
| 526 // - |BeforeAnchor| |
| 527 // - |BeforeChildren| |
| 528 // - |OffsetInContainer| for |Text| |
| 529 // - |OffsetInContainer| for offset:0 and anchor node w/o children. |
| 530 // Other anchor types of |position| are transformed to equivalent position. |
| 531 // Note: We should not normalize |position|, caret position can be different |
| 532 // for after node and before node, e.g. |
| 533 // "hello" <img> |
| 534 // afterNode("hello") and beforeNode(<img>) point to between "hello" and |
| 535 // <img>. |
| 536 template <typename Strategy> |
| 537 LayoutPosition LayoutPosition::createAlgorithm(const PositionTemplate<Strategy>&
position) |
| 538 { |
| 539 if (position.isNull()) |
| 540 return LayoutPosition(); |
| 541 DCHECK(position.inShadowIncludingDocument()) << position; |
| 542 DCHECK(position.document()->isActive()) << position; |
| 543 DCHECK_GE(position.document()->lifecycle().state(), DocumentLifecycle::Layou
tClean) << position; |
| 544 Node* const anchorNode = position.anchorNode(); |
| 545 LayoutObject* const layoutObject = anchorNode->layoutObject(); |
| 546 if (!layoutObject) |
| 547 return LayoutPosition(); |
| 548 switch (position.anchorType()) { |
| 549 case PositionAnchorType::AfterAnchor: |
| 550 return afterAnchorOf(layoutObject); |
| 551 case PositionAnchorType::AfterChildren: |
| 552 DCHECK(layoutObject->canHaveChildren()) << layoutObject; |
| 553 if (LayoutObject* lastChild = LayoutTreeTraversal::lastChild(*layoutObje
ct)) |
| 554 return afterAnchorOf(lastChild); |
| 555 return beforeChildrenOf(layoutObject); |
| 556 case PositionAnchorType::BeforeAnchor: |
| 557 return beforeAnchorOf(layoutObject); |
| 558 case PositionAnchorType::BeforeChildren: |
| 559 DCHECK(layoutObject->canHaveChildren()) << layoutObject; |
| 560 if (LayoutObject* firstChild = LayoutTreeTraversal::firstChild(*layoutOb
ject)) |
| 561 return beforeAnchorOf(firstChild); |
| 562 return beforeChildrenOf(layoutObject); |
| 563 case PositionAnchorType::OffsetInAnchor: { |
| 564 const int offsetInNode = position.offsetInContainerNode(); |
| 565 if (anchorNode->isTextNode()) { |
| 566 LayoutText* const layoutText = toLayoutText(layoutObject); |
| 567 if (!layoutText->firstTextBox()) |
| 568 return offsetInNode == 0 ? beforeAnchorOf(layoutObject) : afterA
nchorOf(layoutObject); |
| 569 const InlineBoxPosition& boxPosition = findInlineBoxPosition(*toText
(anchorNode), offsetInNode); |
| 570 if (boxPosition.inlineBox) |
| 571 return LayoutPosition(boxPosition); |
| 572 NOTREACHED() << "Why no inlineBox? " << anchorNode << ' ' << offsetI
nNode; |
| 573 return LayoutPosition(); |
| 574 } |
| 575 if (!LayoutTreeTraversal::hasChildren(*layoutObject)) |
| 576 return beforeChildrenOf(layoutObject); |
| 577 if (Node* child = Strategy::childAt(*anchorNode, offsetInNode)) { |
| 578 if (child->isTextNode()) |
| 579 return create(Position(child, 0)); |
| 580 return create(PositionTemplate<Strategy>::beforeNode(child)); |
| 581 } |
| 582 if (Node* lastChild = Strategy::lastChild(*anchorNode)) { |
| 583 if (lastChild->isTextNode()) |
| 584 return create(PositionTemplate<Strategy>(lastChild, toText(lastC
hild)->length())); |
| 585 return create(PositionTemplate<Strategy>::afterNode(lastChild)); |
| 586 } |
| 587 return beforeChildrenOf(layoutObject); |
| 588 } |
| 589 } |
| 590 NOTREACHED() << "Invalid anchor type " << position; |
| 591 return LayoutPosition(); |
| 592 } |
| 593 |
| 594 // static |
| 595 LayoutPosition LayoutPosition::create(const Position& position) |
| 596 { |
| 597 return createAlgorithm<EditingStrategy>(position); |
| 598 } |
| 599 |
| 600 // static |
| 601 LayoutPosition LayoutPosition::create(const PositionInFlatTree& position) |
| 602 { |
| 603 return createAlgorithm<EditingInFlatTreeStrategy>(position); |
| 604 } |
| 605 |
| 606 // static |
| 607 LayoutObject* LayoutPosition::layoutObjectOf(const InlineBox& inlineBox) |
| 608 { |
| 609 return inlineBox.getLineLayoutItem().layoutObject(); |
| 610 } |
| 611 |
| 612 LayoutPosition leftPositionOf(const LayoutPosition& position) |
| 613 { |
| 614 if (position.isNull()) |
| 615 return LayoutPosition(); |
| 616 if (position.isLeftToRightDirection()) |
| 617 return previousPositionOf(position); |
| 618 return nextPositionOf(position); |
| 619 } |
| 620 |
| 621 // |nextPositionOf()| returns a next position of |position| by Unicode |
| 622 // code unit, 16-bit quantity. Callers of this function should handle |
| 623 // Unicode surrogate pair. |
| 624 LayoutPosition nextPositionOf(const LayoutPosition& position) |
| 625 { |
| 626 DCHECK(position.isValid()) << position; |
| 627 LayoutObject* const layoutObject = position.m_layoutObject; |
| 628 switch (position.m_anchorType) { |
| 629 case PositionAnchorType::AfterAnchor: |
| 630 if (LayoutObject* nextSibling = LayoutTreeTraversal::nextSibling(*layout
Object)) { |
| 631 // <a></a>|<b></b> => <a></a>|<b></b> |
| 632 return LayoutPosition::beforeAnchorOf(nextSibling); |
| 633 } |
| 634 if (LayoutObject* parent = LayoutTreeTraversal::parent(*layoutObject)) { |
| 635 // <a><b></b>|</a> => <a><b></b></a>| |
| 636 return LayoutPosition::afterAnchorOf(parent); |
| 637 } |
| 638 return LayoutPosition(); |
| 639 case PositionAnchorType::BeforeAnchor: |
| 640 if (LayoutObject* firstChild = LayoutTreeTraversal::firstChild(*layoutOb
ject)) { |
| 641 // |<a>x</a> => <a>|x</a> |
| 642 return LayoutPosition::beforeAnchorOf(firstChild); |
| 643 } |
| 644 if (layoutObject->canHaveChildren()) { |
| 645 // |<a></a> => <a>|</a> |
| 646 return LayoutPosition::beforeChildrenOf(layoutObject); |
| 647 } |
| 648 // |<br> => <br>| |
| 649 return LayoutPosition::afterAnchorOf(layoutObject); |
| 650 case PositionAnchorType::BeforeChildren: |
| 651 // <a>|</a> => <a></a>| |
| 652 return LayoutPosition::afterAnchorOf(layoutObject); |
| 653 case PositionAnchorType::OffsetInAnchor: { |
| 654 DCHECK(position.m_inlineBox); |
| 655 InlineTextBox* const inlineTextBox = toInlineTextBox(position.m_inlineBo
x); |
| 656 const int newOffset = position.m_offsetInBox + 1; |
| 657 // Note: InlineTextBox::end() == start() + length() - 1 |
| 658 if (static_cast<unsigned>(newOffset) <= inlineTextBox->end() + 1) |
| 659 return LayoutPosition(InlineBoxPosition(inlineTextBox, newOffset)); |
| 660 |
| 661 if (InlineBox* nextLeafChild = position.m_inlineBox->nextLeafChild()) { |
| 662 // Move within current |RootInlineBox| or next |RootInlineBox| |
| 663 return LayoutPosition::beforeAnchorOf(nextLeafChild); |
| 664 } |
| 665 |
| 666 if (const RootInlineBox* nextRoot = position.m_inlineBox->root().nextRoo
tBox()) { |
| 667 // Move to first |InlineBox| of next line. |
| 668 return LayoutPosition::beforeAnchorOf(nextRoot->firstChild()); |
| 669 } |
| 670 |
| 671 // Below logic is as same as |AfterAnchor|. |
| 672 if (LayoutObject* nextSibling = LayoutTreeTraversal::nextSibling(*layout
Object)) { |
| 673 // foo|<a></a> => foo|<a></a> |
| 674 return LayoutPosition::beforeAnchorOf(nextSibling); |
| 675 } |
| 676 if (LayoutObject* parent = LayoutTreeTraversal::parent(*layoutObject)) { |
| 677 // <a>foo|</a> => <a>foo</a>| |
| 678 return LayoutPosition::afterAnchorOf(parent); |
| 679 } |
| 680 return LayoutPosition(); |
| 681 } |
| 682 } |
| 683 NOTREACHED() << "Invalid anchor type " << position; |
| 684 return LayoutPosition(); |
| 685 } |
| 686 |
| 687 LayoutPosition LayoutPosition::previousPositionOf(LayoutObject* layoutObject) |
| 688 { |
| 689 LayoutObject* const previous = LayoutTreeTraversal::previous(*layoutObject); |
| 690 if (!previous) |
| 691 return LayoutPosition(); |
| 692 if (previous == LayoutTreeTraversal::parent(*layoutObject)) { |
| 693 // |layoutObject| is a first child of parent. The previous position is |
| 694 // before parent. |
| 695 return LayoutPosition::beforeAnchorOf(previous); |
| 696 } |
| 697 return LayoutPosition::afterAnchorOf(previous); |
| 698 } |
| 699 |
| 700 // |previousPositionOf()| returns a previous position of |position| by Unicode |
| 701 // code unit, 16-bit quantity. Callers of this function should handle |
| 702 // Unicode surrogate pair. |
| 703 LayoutPosition previousPositionOf(const LayoutPosition& position) |
| 704 { |
| 705 DCHECK(position.isValid()) << position; |
| 706 LayoutObject* const layoutObject = position.m_layoutObject; |
| 707 switch (position.m_anchorType) { |
| 708 case PositionAnchorType::AfterAnchor: |
| 709 if (LayoutObject* lastChild = LayoutTreeTraversal::lastChild(*layoutObje
ct)) |
| 710 return LayoutPosition::afterAnchorOf(lastChild); |
| 711 return LayoutPosition::beforeAnchorOf(layoutObject); |
| 712 case PositionAnchorType::BeforeAnchor: |
| 713 case PositionAnchorType::BeforeChildren: |
| 714 return LayoutPosition::previousPositionOf(layoutObject); |
| 715 case PositionAnchorType::OffsetInAnchor: |
| 716 DCHECK(position.m_inlineBox) << position; |
| 717 if (position.isOffsetInAnchor() && position.m_offsetInBox > 0) { |
| 718 InlineTextBox* const inlineTextBox = toInlineTextBox(position.m_inli
neBox); |
| 719 const int newOffset = position.m_offsetInBox - 1; |
| 720 if (static_cast<unsigned>(newOffset) >= inlineTextBox->start()) |
| 721 return LayoutPosition(InlineBoxPosition(inlineTextBox, newOffset
)); |
| 722 } |
| 723 if (InlineBox* previousLeafChild = position.m_inlineBox->prevLeafChild()
) { |
| 724 #if 0 |
| 725 if (shouldUseOffsetInAnchor(*previousLeafChild)) { |
| 726 // In case of adjacent Text node, we move to before end of |
| 727 // character instead of after previous Text node, e.g. |
| 728 // <S>ab</S><U>|cd</U> => <S>a|b</S><U>cd</U>, |
| 729 // instead of <S>ab|</S><U>cd</U> |
| 730 return LayoutPosition(InlineBoxPosition(previousLeafChild, toInl
ineTextBox(previousLeafChild)->end())); |
| 731 } |
| 732 #endif |
| 733 return LayoutPosition::afterAnchorOf(previousLeafChild); |
| 734 } |
| 735 |
| 736 if (const RootInlineBox* previousRoot = position.m_inlineBox->root().pre
vRootBox()) { |
| 737 // Move to last |InlineBox| of previous line. |
| 738 return LayoutPosition::afterAnchorOf(previousRoot->lastChild()); |
| 739 } |
| 740 |
| 741 return LayoutPosition::previousPositionOf(layoutObject); |
| 742 } |
| 743 NOTREACHED() << "Invalid anchor type " << position; |
| 744 return LayoutPosition(); |
| 745 } |
| 746 |
| 747 LayoutPosition rightPositionOf(const LayoutPosition& position) |
| 748 { |
| 749 if (position.isNull()) |
| 750 return LayoutPosition(); |
| 751 if (position.isLeftToRightDirection()) |
| 752 return nextPositionOf(position); |
| 753 return previousPositionOf(position); |
| 754 } |
| 755 |
| 756 class LayoutPositionPrinter final { |
| 757 STATIC_ONLY(LayoutPositionPrinter); |
| 758 |
| 759 public: |
| 760 // |LayoutPosition| member variable getters |
| 761 static PositionAnchorType anchorTypeOf(const LayoutPosition&); |
| 762 static String nameOf(const LayoutPosition&); |
| 763 static int textOffsetOf(const LayoutPosition&); |
| 764 }; |
| 765 |
| 766 // static |
| 767 PositionAnchorType LayoutPositionPrinter::anchorTypeOf(const LayoutPosition& pos
ition) |
| 768 { |
| 769 return position.m_anchorType; |
| 770 } |
| 771 |
| 772 // static |
| 773 String LayoutPositionPrinter::nameOf(const LayoutPosition& position) |
| 774 { |
| 775 if (position.isNull()) |
| 776 return emptyString(); |
| 777 if (position.m_inlineBox) |
| 778 return position.m_inlineBox->debugName(); |
| 779 return position.m_layoutObject->debugName(); |
| 780 } |
| 781 |
| 782 // static |
| 783 int LayoutPositionPrinter::textOffsetOf(const LayoutPosition& position) |
| 784 { |
| 785 ASSERT(position.isOffsetInAnchor()); |
| 786 return position.m_offsetInBox; |
| 787 } |
| 788 |
| 789 std::ostream& operator<<(std::ostream& ostream, const LayoutPosition& position) |
| 790 { |
| 791 if (position.isNull()) |
| 792 return ostream << "null"; |
| 793 ostream << LayoutPositionPrinter::nameOf(position) << '@'; |
| 794 if (position.isOffsetInAnchor()) |
| 795 return ostream << std::dec << LayoutPositionPrinter::textOffsetOf(positi
on); |
| 796 return ostream << LayoutPositionPrinter::anchorTypeOf(position); |
| 797 } |
| 798 |
| 799 } // namespace blink |
| OLD | NEW |