Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: third_party/WebKit/Source/core/editing/LayoutPosition.cpp

Issue 1756763002: 2016-03-02T18:53:01 LayoutPosition Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 2016-04-25T19:02:18 Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/LayoutPosition.h ('k') | third_party/WebKit/Source/core/editing/LayoutPositionTest.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698