OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All r
ights reserved. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All |
| 3 * rights reserved. |
3 * Copyright (C) 2005 Alexey Proskuryakov. | 4 * Copyright (C) 2005 Alexey Proskuryakov. |
4 * | 5 * |
5 * Redistribution and use in source and binary forms, with or without | 6 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 7 * modification, are permitted provided that the following conditions |
7 * are met: | 8 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 9 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 10 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 11 * 2. Redistributions in binary form must reproduce the above copyright |
11 * notice, this list of conditions and the following disclaimer in the | 12 * notice, this list of conditions and the following disclaimer in the |
12 * documentation and/or other materials provided with the distribution. | 13 * documentation and/or other materials provided with the distribution. |
(...skipping 143 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
156 m_textBox(nullptr), | 157 m_textBox(nullptr), |
157 m_remainingTextBox(nullptr), | 158 m_remainingTextBox(nullptr), |
158 m_firstLetterText(nullptr), | 159 m_firstLetterText(nullptr), |
159 m_lastTextNode(nullptr), | 160 m_lastTextNode(nullptr), |
160 m_lastTextNodeEndedWithCollapsedSpace(false), | 161 m_lastTextNodeEndedWithCollapsedSpace(false), |
161 m_sortedTextBoxesPosition(0), | 162 m_sortedTextBoxesPosition(0), |
162 m_behavior(adjustBehaviorFlags<Strategy>(behavior)), | 163 m_behavior(adjustBehaviorFlags<Strategy>(behavior)), |
163 m_handledFirstLetter(false), | 164 m_handledFirstLetter(false), |
164 m_shouldStop(false), | 165 m_shouldStop(false), |
165 m_handleShadowRoot(false), | 166 m_handleShadowRoot(false), |
166 // The call to emitsOriginalText() must occur after m_behavior is initiali
zed. | 167 // The call to emitsOriginalText() must occur after m_behavior is |
| 168 // initialized. |
167 m_textState(emitsOriginalText()) { | 169 m_textState(emitsOriginalText()) { |
168 DCHECK(start.isNotNull()); | 170 DCHECK(start.isNotNull()); |
169 DCHECK(end.isNotNull()); | 171 DCHECK(end.isNotNull()); |
170 | 172 |
171 // TODO(dglazkov): TextIterator should not be created for documents that don't
have a frame, | 173 // TODO(dglazkov): TextIterator should not be created for documents that don't |
172 // but it currently still happens in some cases. See http://crbug.com/591877 f
or details. | 174 // have a frame, but it currently still happens in some cases. See |
| 175 // http://crbug.com/591877 for details. |
173 DCHECK(!start.document()->view() || !start.document()->view()->needsLayout()); | 176 DCHECK(!start.document()->view() || !start.document()->view()->needsLayout()); |
174 DCHECK(!start.document()->needsLayoutTreeUpdate()); | 177 DCHECK(!start.document()->needsLayoutTreeUpdate()); |
175 | 178 |
176 if (start.compareTo(end) > 0) { | 179 if (start.compareTo(end) > 0) { |
177 initialize(end.computeContainerNode(), end.computeOffsetInContainerNode(), | 180 initialize(end.computeContainerNode(), end.computeOffsetInContainerNode(), |
178 start.computeContainerNode(), | 181 start.computeContainerNode(), |
179 start.computeOffsetInContainerNode()); | 182 start.computeOffsetInContainerNode()); |
180 return; | 183 return; |
181 } | 184 } |
182 initialize(start.computeContainerNode(), start.computeOffsetInContainerNode(), | 185 initialize(start.computeContainerNode(), start.computeOffsetInContainerNode(), |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
258 return; | 261 return; |
259 | 262 |
260 if (m_node) | 263 if (m_node) |
261 DCHECK(!m_node->document().needsLayoutTreeUpdate()) << m_node; | 264 DCHECK(!m_node->document().needsLayoutTreeUpdate()) << m_node; |
262 | 265 |
263 m_textState.resetRunInformation(); | 266 m_textState.resetRunInformation(); |
264 | 267 |
265 // handle remembered node that needed a newline after the text node's newline | 268 // handle remembered node that needed a newline after the text node's newline |
266 if (m_needsAnotherNewline) { | 269 if (m_needsAnotherNewline) { |
267 // Emit the extra newline, and position it *inside* m_node, after m_node's | 270 // Emit the extra newline, and position it *inside* m_node, after m_node's |
268 // contents, in case it's a block, in the same way that we position the firs
t | 271 // contents, in case it's a block, in the same way that we position the |
269 // newline. The range for the emitted newline should start where the line | 272 // first newline. The range for the emitted newline should start where the |
270 // break begins. | 273 // line break begins. |
271 // FIXME: It would be cleaner if we emitted two newlines during the last | 274 // FIXME: It would be cleaner if we emitted two newlines during the last |
272 // iteration, instead of using m_needsAnotherNewline. | 275 // iteration, instead of using m_needsAnotherNewline. |
273 Node* lastChild = Strategy::lastChild(*m_node); | 276 Node* lastChild = Strategy::lastChild(*m_node); |
274 Node* baseNode = lastChild ? lastChild : m_node.get(); | 277 Node* baseNode = lastChild ? lastChild : m_node.get(); |
275 spliceBuffer('\n', Strategy::parent(*baseNode), baseNode, 1, 1); | 278 spliceBuffer('\n', Strategy::parent(*baseNode), baseNode, 1, 1); |
276 m_needsAnotherNewline = false; | 279 m_needsAnotherNewline = false; |
277 return; | 280 return; |
278 } | 281 } |
279 | 282 |
280 if (!m_textBox && m_remainingTextBox) { | 283 if (!m_textBox && m_remainingTextBox) { |
(...skipping 20 matching lines...) Expand all Loading... |
301 // precedes the element | 304 // precedes the element |
302 if (m_node == m_endContainer && !m_endOffset) { | 305 if (m_node == m_endContainer && !m_endOffset) { |
303 representNodeOffsetZero(); | 306 representNodeOffsetZero(); |
304 m_node = nullptr; | 307 m_node = nullptr; |
305 return; | 308 return; |
306 } | 309 } |
307 | 310 |
308 LayoutObject* layoutObject = m_node->layoutObject(); | 311 LayoutObject* layoutObject = m_node->layoutObject(); |
309 if (!layoutObject) { | 312 if (!layoutObject) { |
310 if (m_node->isShadowRoot()) { | 313 if (m_node->isShadowRoot()) { |
311 // A shadow root doesn't have a layoutObject, but we want to visit child
ren anyway. | 314 // A shadow root doesn't have a layoutObject, but we want to visit |
| 315 // children anyway. |
312 m_iterationProgress = m_iterationProgress < HandledNode | 316 m_iterationProgress = m_iterationProgress < HandledNode |
313 ? HandledNode | 317 ? HandledNode |
314 : m_iterationProgress; | 318 : m_iterationProgress; |
315 m_handleShadowRoot = true; | 319 m_handleShadowRoot = true; |
316 } else { | 320 } else { |
317 m_iterationProgress = HandledChildren; | 321 m_iterationProgress = HandledChildren; |
318 } | 322 } |
319 } else { | 323 } else { |
320 // Enter author shadow roots, from youngest, if any and if necessary. | 324 // Enter author shadow roots, from youngest, if any and if necessary. |
321 if (m_iterationProgress < HandledOpenShadowRoots) { | 325 if (m_iterationProgress < HandledOpenShadowRoots) { |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
381 // | 385 // |
382 // 1. Iterate over child nodes, if we haven't done yet. | 386 // 1. Iterate over child nodes, if we haven't done yet. |
383 // To support |TextIteratorEmitsImageAltText|, we don't traversal child | 387 // To support |TextIteratorEmitsImageAltText|, we don't traversal child |
384 // nodes, in flat tree. | 388 // nodes, in flat tree. |
385 Node* next = | 389 Node* next = |
386 m_iterationProgress < HandledChildren && !isHTMLImageElement(*m_node) | 390 m_iterationProgress < HandledChildren && !isHTMLImageElement(*m_node) |
387 ? Strategy::firstChild(*m_node) | 391 ? Strategy::firstChild(*m_node) |
388 : nullptr; | 392 : nullptr; |
389 m_offset = 0; | 393 m_offset = 0; |
390 if (!next) { | 394 if (!next) { |
391 // 2. If we've already iterated children or they are not available, go to
the next sibling node. | 395 // 2. If we've already iterated children or they are not available, go to |
| 396 // the next sibling node. |
392 next = Strategy::nextSibling(*m_node); | 397 next = Strategy::nextSibling(*m_node); |
393 if (!next) { | 398 if (!next) { |
394 // 3. If we are at the last child, go up the node tree until we find a n
ext sibling. | 399 // 3. If we are at the last child, go up the node tree until we find a |
| 400 // next sibling. |
395 ContainerNode* parentNode = Strategy::parent(*m_node); | 401 ContainerNode* parentNode = Strategy::parent(*m_node); |
396 while (!next && parentNode) { | 402 while (!next && parentNode) { |
397 if (m_node == m_endNode || | 403 if (m_node == m_endNode || |
398 Strategy::isDescendantOf(*m_endContainer, *parentNode)) | 404 Strategy::isDescendantOf(*m_endContainer, *parentNode)) |
399 return; | 405 return; |
400 bool haveLayoutObject = m_node->layoutObject(); | 406 bool haveLayoutObject = m_node->layoutObject(); |
401 m_node = parentNode; | 407 m_node = parentNode; |
402 m_fullyClippedStack.pop(); | 408 m_fullyClippedStack.pop(); |
403 parentNode = Strategy::parent(*m_node); | 409 parentNode = Strategy::parent(*m_node); |
404 if (haveLayoutObject) | 410 if (haveLayoutObject) |
405 exitNode(); | 411 exitNode(); |
406 if (m_textState.positionNode()) { | 412 if (m_textState.positionNode()) { |
407 m_iterationProgress = HandledChildren; | 413 m_iterationProgress = HandledChildren; |
408 return; | 414 return; |
409 } | 415 } |
410 next = Strategy::nextSibling(*m_node); | 416 next = Strategy::nextSibling(*m_node); |
411 } | 417 } |
412 | 418 |
413 if (!next && !parentNode && m_shadowDepth > 0) { | 419 if (!next && !parentNode && m_shadowDepth > 0) { |
414 // 4. Reached the top of a shadow root. If it's created by author, the
n try to visit the next | 420 // 4. Reached the top of a shadow root. If it's created by author, |
| 421 // then try to visit the next |
415 // sibling shadow root, if any. | 422 // sibling shadow root, if any. |
416 if (!m_node->isShadowRoot()) { | 423 if (!m_node->isShadowRoot()) { |
417 NOTREACHED(); | 424 NOTREACHED(); |
418 m_shouldStop = true; | 425 m_shouldStop = true; |
419 return; | 426 return; |
420 } | 427 } |
421 ShadowRoot* shadowRoot = toShadowRoot(m_node); | 428 ShadowRoot* shadowRoot = toShadowRoot(m_node); |
422 if (shadowRoot->type() == ShadowRootType::V0 || | 429 if (shadowRoot->type() == ShadowRootType::V0 || |
423 shadowRoot->type() == ShadowRootType::Open) { | 430 shadowRoot->type() == ShadowRootType::Open) { |
424 ShadowRoot* nextShadowRoot = shadowRoot->olderShadowRoot(); | 431 ShadowRoot* nextShadowRoot = shadowRoot->olderShadowRoot(); |
425 if (nextShadowRoot && | 432 if (nextShadowRoot && |
426 nextShadowRoot->type() == ShadowRootType::V0) { | 433 nextShadowRoot->type() == ShadowRootType::V0) { |
427 m_fullyClippedStack.pop(); | 434 m_fullyClippedStack.pop(); |
428 m_node = nextShadowRoot; | 435 m_node = nextShadowRoot; |
429 m_iterationProgress = HandledNone; | 436 m_iterationProgress = HandledNone; |
430 // m_shadowDepth is unchanged since we exit from a shadow root and
enter another. | 437 // m_shadowDepth is unchanged since we exit from a shadow root and |
| 438 // enter another. |
431 m_fullyClippedStack.pushFullyClippedState(m_node); | 439 m_fullyClippedStack.pushFullyClippedState(m_node); |
432 } else { | 440 } else { |
433 // We are the last shadow root; exit from here and go back to wher
e we were. | 441 // We are the last shadow root; exit from here and go back to |
| 442 // where we were. |
434 m_node = &shadowRoot->host(); | 443 m_node = &shadowRoot->host(); |
435 m_iterationProgress = HandledOpenShadowRoots; | 444 m_iterationProgress = HandledOpenShadowRoots; |
436 --m_shadowDepth; | 445 --m_shadowDepth; |
437 m_fullyClippedStack.pop(); | 446 m_fullyClippedStack.pop(); |
438 } | 447 } |
439 } else { | 448 } else { |
440 // If we are in a closed or user-agent shadow root, then go back to
the host. | 449 // If we are in a closed or user-agent shadow root, then go back to |
441 // TODO(kochi): Make sure we treat closed shadow as user agent shado
w here. | 450 // the host. |
| 451 // TODO(kochi): Make sure we treat closed shadow as user agent |
| 452 // shadow here. |
442 DCHECK(shadowRoot->type() == ShadowRootType::Closed || | 453 DCHECK(shadowRoot->type() == ShadowRootType::Closed || |
443 shadowRoot->type() == ShadowRootType::UserAgent); | 454 shadowRoot->type() == ShadowRootType::UserAgent); |
444 m_node = &shadowRoot->host(); | 455 m_node = &shadowRoot->host(); |
445 m_iterationProgress = HandledUserAgentShadowRoot; | 456 m_iterationProgress = HandledUserAgentShadowRoot; |
446 --m_shadowDepth; | 457 --m_shadowDepth; |
447 m_fullyClippedStack.pop(); | 458 m_fullyClippedStack.pop(); |
448 } | 459 } |
449 m_handledFirstLetter = false; | 460 m_handledFirstLetter = false; |
450 m_firstLetterText = nullptr; | 461 m_firstLetterText = nullptr; |
451 continue; | 462 continue; |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
588 size_t subrunEnd) { | 599 size_t subrunEnd) { |
589 if (nextTextBox || !m_textBox->root().nextRootBox() || | 600 if (nextTextBox || !m_textBox->root().nextRootBox() || |
590 m_textBox->root().lastChild() != m_textBox) | 601 m_textBox->root().lastChild() != m_textBox) |
591 return subrunEnd; | 602 return subrunEnd; |
592 | 603 |
593 const String& text = toLayoutText(m_node->layoutObject())->text(); | 604 const String& text = toLayoutText(m_node->layoutObject())->text(); |
594 if (text.endsWith(' ') == 0 || subrunEnd != text.length() - 1 || | 605 if (text.endsWith(' ') == 0 || subrunEnd != text.length() - 1 || |
595 text[subrunEnd - 1] == ' ') | 606 text[subrunEnd - 1] == ' ') |
596 return subrunEnd; | 607 return subrunEnd; |
597 | 608 |
598 // If there is the leading space in the next line, we don't need to restore th
e trailing space. | 609 // If there is the leading space in the next line, we don't need to restore |
| 610 // the trailing space. |
599 // Example: <div style="width: 2em;"><b><i>foo </i></b> bar</div> | 611 // Example: <div style="width: 2em;"><b><i>foo </i></b> bar</div> |
600 InlineBox* firstBoxOfNextLine = m_textBox->root().nextRootBox()->firstChild(); | 612 InlineBox* firstBoxOfNextLine = m_textBox->root().nextRootBox()->firstChild(); |
601 if (!firstBoxOfNextLine) | 613 if (!firstBoxOfNextLine) |
602 return subrunEnd + 1; | 614 return subrunEnd + 1; |
603 Node* firstNodeOfNextLine = firstBoxOfNextLine->getLineLayoutItem().node(); | 615 Node* firstNodeOfNextLine = firstBoxOfNextLine->getLineLayoutItem().node(); |
604 if (!firstNodeOfNextLine || firstNodeOfNextLine->nodeValue()[0] != ' ') | 616 if (!firstNodeOfNextLine || firstNodeOfNextLine->nodeValue()[0] != ' ') |
605 return subrunEnd + 1; | 617 return subrunEnd + 1; |
606 | 618 |
607 return subrunEnd; | 619 return subrunEnd; |
608 } | 620 } |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 !(nextTextBox->getLineLayoutItem().isEqual(layoutObject))) { | 703 !(nextTextBox->getLineLayoutItem().isEqual(layoutObject))) { |
692 m_textBox = 0; | 704 m_textBox = 0; |
693 return; | 705 return; |
694 } | 706 } |
695 DCHECK(!nextTextBox || | 707 DCHECK(!nextTextBox || |
696 nextTextBox->getLineLayoutItem().isEqual(layoutObject)); | 708 nextTextBox->getLineLayoutItem().isEqual(layoutObject)); |
697 | 709 |
698 if (runStart < runEnd) { | 710 if (runStart < runEnd) { |
699 // Handle either a single newline character (which becomes a space), | 711 // Handle either a single newline character (which becomes a space), |
700 // or a run of characters that does not include a newline. | 712 // or a run of characters that does not include a newline. |
701 // This effectively translates newlines to spaces without copying the te
xt. | 713 // This effectively translates newlines to spaces without copying the |
| 714 // text. |
702 if (str[runStart] == '\n') { | 715 if (str[runStart] == '\n') { |
703 // We need to preserve new lines in case of PRE_LINE. | 716 // We need to preserve new lines in case of PRE_LINE. |
704 // See bug crbug.com/317365. | 717 // See bug crbug.com/317365. |
705 if (layoutObject->style()->whiteSpace() == PRE_LINE) | 718 if (layoutObject->style()->whiteSpace() == PRE_LINE) |
706 spliceBuffer('\n', m_node, 0, runStart, runStart); | 719 spliceBuffer('\n', m_node, 0, runStart, runStart); |
707 else | 720 else |
708 spliceBuffer(spaceCharacter, m_node, 0, runStart, runStart + 1); | 721 spliceBuffer(spaceCharacter, m_node, 0, runStart, runStart + 1); |
709 m_offset = runStart + 1; | 722 m_offset = runStart + 1; |
710 } else { | 723 } else { |
711 size_t subrunEnd = str.find('\n', runStart); | 724 size_t subrunEnd = str.find('\n', runStart); |
712 if (subrunEnd == kNotFound || subrunEnd > runEnd) { | 725 if (subrunEnd == kNotFound || subrunEnd > runEnd) { |
713 subrunEnd = runEnd; | 726 subrunEnd = runEnd; |
714 runStart = restoreCollapsedLeadingSpace(runStart); | 727 runStart = restoreCollapsedLeadingSpace(runStart); |
715 subrunEnd = restoreCollapsedTrailingSpace(nextTextBox, subrunEnd); | 728 subrunEnd = restoreCollapsedTrailingSpace(nextTextBox, subrunEnd); |
716 } | 729 } |
717 | 730 |
718 m_offset = subrunEnd; | 731 m_offset = subrunEnd; |
719 emitText(m_node, layoutObject, runStart, subrunEnd); | 732 emitText(m_node, layoutObject, runStart, subrunEnd); |
720 } | 733 } |
721 | 734 |
722 // If we are doing a subrun that doesn't go to the end of the text box, | 735 // If we are doing a subrun that doesn't go to the end of the text box, |
723 // come back again to finish handling this text box; don't advance to th
e next one. | 736 // come back again to finish handling this text box; don't advance to |
| 737 // the next one. |
724 if (static_cast<unsigned>(m_textState.positionEndOffset()) < textBoxEnd) | 738 if (static_cast<unsigned>(m_textState.positionEndOffset()) < textBoxEnd) |
725 return; | 739 return; |
726 | 740 |
727 // Advance and return | 741 // Advance and return |
728 unsigned nextRunStart = | 742 unsigned nextRunStart = |
729 nextTextBox ? nextTextBox->start() : str.length(); | 743 nextTextBox ? nextTextBox->start() : str.length(); |
730 if (nextRunStart > runEnd) | 744 if (nextRunStart > runEnd) |
731 m_lastTextNodeEndedWithCollapsedSpace = | 745 m_lastTextNodeEndedWithCollapsedSpace = |
732 true; // collapsed space between runs or at the end | 746 true; // collapsed space between runs or at the end |
733 | 747 |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
958 int bottomMargin = toLayoutBox(r)->collapsedMarginAfter().toInt(); | 972 int bottomMargin = toLayoutBox(r)->collapsedMarginAfter().toInt(); |
959 int fontSize = style->getFontDescription().computedPixelSize(); | 973 int fontSize = style->getFontDescription().computedPixelSize(); |
960 if (bottomMargin * 2 >= fontSize) | 974 if (bottomMargin * 2 >= fontSize) |
961 return true; | 975 return true; |
962 } | 976 } |
963 } | 977 } |
964 | 978 |
965 return false; | 979 return false; |
966 } | 980 } |
967 | 981 |
968 // Whether or not we should emit a character as we enter m_node (if it's a conta
iner) or as we hit it (if it's atomic). | 982 // Whether or not we should emit a character as we enter m_node (if it's a |
| 983 // container) or as we hit it (if it's atomic). |
969 template <typename Strategy> | 984 template <typename Strategy> |
970 bool TextIteratorAlgorithm<Strategy>::shouldRepresentNodeOffsetZero() { | 985 bool TextIteratorAlgorithm<Strategy>::shouldRepresentNodeOffsetZero() { |
971 if (emitsCharactersBetweenAllVisiblePositions() && | 986 if (emitsCharactersBetweenAllVisiblePositions() && |
972 isDisplayInsideTable(m_node)) | 987 isDisplayInsideTable(m_node)) |
973 return true; | 988 return true; |
974 | 989 |
975 // Leave element positioned flush with start of a paragraph | 990 // Leave element positioned flush with start of a paragraph |
976 // (e.g. do not insert tab before a table cell at the start of a paragraph) | 991 // (e.g. do not insert tab before a table cell at the start of a paragraph) |
977 if (m_textState.lastCharacter() == '\n') | 992 if (m_textState.lastCharacter() == '\n') |
978 return false; | 993 return false; |
979 | 994 |
980 // Otherwise, show the position if we have emitted any characters | 995 // Otherwise, show the position if we have emitted any characters |
981 if (m_textState.hasEmitted()) | 996 if (m_textState.hasEmitted()) |
982 return true; | 997 return true; |
983 | 998 |
984 // We've not emitted anything yet. Generally, there is no need for any positio
ning then. | 999 // We've not emitted anything yet. Generally, there is no need for any |
985 // The only exception is when the element is visually not in the same line as | 1000 // positioning then. The only exception is when the element is visually not in |
986 // the start of the range (e.g. the range starts at the end of the previous pa
ragraph). | 1001 // the same line as the start of the range (e.g. the range starts at the end |
987 // NOTE: Creating VisiblePositions and comparing them is relatively expensive,
so we | 1002 // of the previous paragraph). |
988 // make quicker checks to possibly avoid that. Another check that we could mak
e is | 1003 // NOTE: Creating VisiblePositions and comparing them is relatively expensive, |
989 // is whether the inline vs block flow changed since the previous visible elem
ent. | 1004 // so we make quicker checks to possibly avoid that. Another check that we |
990 // I think we're already in a special enough case that that won't be needed, t
ho. | 1005 // could make is is whether the inline vs block flow changed since the |
| 1006 // previous visible element. I think we're already in a special enough case |
| 1007 // that that won't be needed, tho. |
991 | 1008 |
992 // No character needed if this is the first node in the range. | 1009 // No character needed if this is the first node in the range. |
993 if (m_node == m_startContainer) | 1010 if (m_node == m_startContainer) |
994 return false; | 1011 return false; |
995 | 1012 |
996 // If we are outside the start container's subtree, assume we need to emit. | 1013 // If we are outside the start container's subtree, assume we need to emit. |
997 // FIXME: m_startContainer could be an inline block | 1014 // FIXME: m_startContainer could be an inline block |
998 if (!Strategy::isDescendantOf(*m_node, *m_startContainer)) | 1015 if (!Strategy::isDescendantOf(*m_node, *m_startContainer)) |
999 return true; | 1016 return true; |
1000 | 1017 |
1001 // If we started as m_startContainer offset 0 and the current node is a descen
dant of | 1018 // If we started as m_startContainer offset 0 and the current node is a |
1002 // the start container, we already had enough context to correctly decide whet
her to | 1019 // descendant of the start container, we already had enough context to |
1003 // emit after a preceding block. We chose not to emit (m_hasEmitted is false), | 1020 // correctly decide whether to emit after a preceding block. We chose not to |
1004 // so don't second guess that now. | 1021 // emit (m_hasEmitted is false), so don't second guess that now. |
1005 // NOTE: Is this really correct when m_node is not a leftmost descendant? Prob
ably | 1022 // NOTE: Is this really correct when m_node is not a leftmost descendant? |
1006 // immaterial since we likely would have already emitted something by now. | 1023 // Probably immaterial since we likely would have already emitted something by |
| 1024 // now. |
1007 if (!m_startOffset) | 1025 if (!m_startOffset) |
1008 return false; | 1026 return false; |
1009 | 1027 |
1010 // If this node is unrendered or invisible the VisiblePosition checks below wo
n't have much meaning. | 1028 // If this node is unrendered or invisible the VisiblePosition checks below |
1011 // Additionally, if the range we are iterating over contains huge sections of
unrendered content, | 1029 // won't have much meaning. |
1012 // we would create VisiblePositions on every call to this function without thi
s check. | 1030 // Additionally, if the range we are iterating over contains huge sections of |
| 1031 // unrendered content, we would create VisiblePositions on every call to this |
| 1032 // function without this check. |
1013 if (!m_node->layoutObject() || | 1033 if (!m_node->layoutObject() || |
1014 m_node->layoutObject()->style()->visibility() != EVisibility::Visible || | 1034 m_node->layoutObject()->style()->visibility() != EVisibility::Visible || |
1015 (m_node->layoutObject()->isLayoutBlockFlow() && | 1035 (m_node->layoutObject()->isLayoutBlockFlow() && |
1016 !toLayoutBlock(m_node->layoutObject())->size().height() && | 1036 !toLayoutBlock(m_node->layoutObject())->size().height() && |
1017 !isHTMLBodyElement(*m_node))) | 1037 !isHTMLBodyElement(*m_node))) |
1018 return false; | 1038 return false; |
1019 | 1039 |
1020 // The startPos.isNotNull() check is needed because the start could be before
the body, | 1040 // The startPos.isNotNull() check is needed because the start could be before |
1021 // and in that case we'll get null. We don't want to put in newlines at the st
art in that case. | 1041 // the body, and in that case we'll get null. We don't want to put in newlines |
1022 // The currPos.isNotNull() check is needed because positions in non-HTML conte
nt | 1042 // at the start in that case. |
1023 // (like SVG) do not have visible positions, and we don't want to emit for the
m either. | 1043 // The currPos.isNotNull() check is needed because positions in non-HTML |
| 1044 // content (like SVG) do not have visible positions, and we don't want to emit |
| 1045 // for them either. |
1024 VisiblePosition startPos = | 1046 VisiblePosition startPos = |
1025 createVisiblePosition(Position(m_startContainer, m_startOffset)); | 1047 createVisiblePosition(Position(m_startContainer, m_startOffset)); |
1026 VisiblePosition currPos = VisiblePosition::beforeNode(m_node); | 1048 VisiblePosition currPos = VisiblePosition::beforeNode(m_node); |
1027 return startPos.isNotNull() && currPos.isNotNull() && | 1049 return startPos.isNotNull() && currPos.isNotNull() && |
1028 !inSameLine(startPos, currPos); | 1050 !inSameLine(startPos, currPos); |
1029 } | 1051 } |
1030 | 1052 |
1031 template <typename Strategy> | 1053 template <typename Strategy> |
1032 bool TextIteratorAlgorithm<Strategy>::shouldEmitSpaceBeforeAndAfterNode( | 1054 bool TextIteratorAlgorithm<Strategy>::shouldEmitSpaceBeforeAndAfterNode( |
1033 Node* node) { | 1055 Node* node) { |
1034 return isDisplayInsideTable(node) && | 1056 return isDisplayInsideTable(node) && |
1035 (node->layoutObject()->isInline() || | 1057 (node->layoutObject()->isInline() || |
1036 emitsCharactersBetweenAllVisiblePositions()); | 1058 emitsCharactersBetweenAllVisiblePositions()); |
1037 } | 1059 } |
1038 | 1060 |
1039 template <typename Strategy> | 1061 template <typename Strategy> |
1040 void TextIteratorAlgorithm<Strategy>::representNodeOffsetZero() { | 1062 void TextIteratorAlgorithm<Strategy>::representNodeOffsetZero() { |
1041 // Emit a character to show the positioning of m_node. | 1063 // Emit a character to show the positioning of m_node. |
1042 | 1064 |
1043 // When we haven't been emitting any characters, shouldRepresentNodeOffsetZero
() can | 1065 // When we haven't been emitting any characters, |
1044 // create VisiblePositions, which is expensive. So, we perform the inexpensive
checks | 1066 // shouldRepresentNodeOffsetZero() can create VisiblePositions, which is |
1045 // on m_node to see if it necessitates emitting a character first and will ear
ly return | 1067 // expensive. So, we perform the inexpensive checks on m_node to see if it |
1046 // before encountering shouldRepresentNodeOffsetZero()s worse case behavior. | 1068 // necessitates emitting a character first and will early return before |
| 1069 // encountering shouldRepresentNodeOffsetZero()s worse case behavior. |
1047 if (shouldEmitTabBeforeNode(m_node)) { | 1070 if (shouldEmitTabBeforeNode(m_node)) { |
1048 if (shouldRepresentNodeOffsetZero()) | 1071 if (shouldRepresentNodeOffsetZero()) |
1049 spliceBuffer('\t', Strategy::parent(*m_node), m_node, 0, 0); | 1072 spliceBuffer('\t', Strategy::parent(*m_node), m_node, 0, 0); |
1050 } else if (shouldEmitNewlineBeforeNode(*m_node)) { | 1073 } else if (shouldEmitNewlineBeforeNode(*m_node)) { |
1051 if (shouldRepresentNodeOffsetZero()) | 1074 if (shouldRepresentNodeOffsetZero()) |
1052 spliceBuffer('\n', Strategy::parent(*m_node), m_node, 0, 0); | 1075 spliceBuffer('\n', Strategy::parent(*m_node), m_node, 0, 0); |
1053 } else if (shouldEmitSpaceBeforeAndAfterNode(m_node)) { | 1076 } else if (shouldEmitSpaceBeforeAndAfterNode(m_node)) { |
1054 if (shouldRepresentNodeOffsetZero()) | 1077 if (shouldRepresentNodeOffsetZero()) |
1055 spliceBuffer(spaceCharacter, Strategy::parent(*m_node), m_node, 0, 0); | 1078 spliceBuffer(spaceCharacter, Strategy::parent(*m_node), m_node, 0, 0); |
1056 } | 1079 } |
1057 } | 1080 } |
1058 | 1081 |
1059 template <typename Strategy> | 1082 template <typename Strategy> |
1060 bool TextIteratorAlgorithm<Strategy>::handleNonTextNode() { | 1083 bool TextIteratorAlgorithm<Strategy>::handleNonTextNode() { |
1061 if (shouldEmitNewlineForNode(m_node, emitsOriginalText())) | 1084 if (shouldEmitNewlineForNode(m_node, emitsOriginalText())) |
1062 spliceBuffer('\n', Strategy::parent(*m_node), m_node, 0, 1); | 1085 spliceBuffer('\n', Strategy::parent(*m_node), m_node, 0, 1); |
1063 else if (emitsCharactersBetweenAllVisiblePositions() && | 1086 else if (emitsCharactersBetweenAllVisiblePositions() && |
1064 m_node->layoutObject() && m_node->layoutObject()->isHR()) | 1087 m_node->layoutObject() && m_node->layoutObject()->isHR()) |
1065 spliceBuffer(spaceCharacter, Strategy::parent(*m_node), m_node, 0, 1); | 1088 spliceBuffer(spaceCharacter, Strategy::parent(*m_node), m_node, 0, 1); |
1066 else | 1089 else |
1067 representNodeOffsetZero(); | 1090 representNodeOffsetZero(); |
1068 | 1091 |
1069 return true; | 1092 return true; |
1070 } | 1093 } |
1071 | 1094 |
1072 template <typename Strategy> | 1095 template <typename Strategy> |
1073 void TextIteratorAlgorithm<Strategy>::exitNode() { | 1096 void TextIteratorAlgorithm<Strategy>::exitNode() { |
1074 // prevent emitting a newline when exiting a collapsed block at beginning of t
he range | 1097 // prevent emitting a newline when exiting a collapsed block at beginning of |
1075 // FIXME: !m_hasEmitted does not necessarily mean there was a collapsed block.
.. it could | 1098 // the range |
1076 // have been an hr (e.g.). Also, a collapsed block could have height (e.g. a t
able) and | 1099 // FIXME: !m_hasEmitted does not necessarily mean there was a collapsed |
1077 // therefore look like a blank line. | 1100 // block... it could have been an hr (e.g.). Also, a collapsed block could |
| 1101 // have height (e.g. a table) and therefore look like a blank line. |
1078 if (!m_textState.hasEmitted()) | 1102 if (!m_textState.hasEmitted()) |
1079 return; | 1103 return; |
1080 | 1104 |
1081 // Emit with a position *inside* m_node, after m_node's contents, in | 1105 // Emit with a position *inside* m_node, after m_node's contents, in |
1082 // case it is a block, because the run should start where the | 1106 // case it is a block, because the run should start where the |
1083 // emitted character is positioned visually. | 1107 // emitted character is positioned visually. |
1084 Node* lastChild = Strategy::lastChild(*m_node); | 1108 Node* lastChild = Strategy::lastChild(*m_node); |
1085 Node* baseNode = lastChild ? lastChild : m_node.get(); | 1109 Node* baseNode = lastChild ? lastChild : m_node.get(); |
1086 // FIXME: This shouldn't require the m_lastTextNode to be true, but we can't c
hange that without making | 1110 // FIXME: This shouldn't require the m_lastTextNode to be true, but we can't |
1087 // the logic in _web_attributedStringFromRange match. We'll get that for free
when we switch to use | 1111 // change that without making the logic in _web_attributedStringFromRange |
1088 // TextIterator in _web_attributedStringFromRange. | 1112 // match. We'll get that for free when we switch to use TextIterator in |
1089 // See <rdar://problem/5428427> for an example of how this mismatch will cause
problems. | 1113 // _web_attributedStringFromRange. See <rdar://problem/5428427> for an example |
| 1114 // of how this mismatch will cause problems. |
1090 if (m_lastTextNode && shouldEmitNewlineAfterNode(*m_node)) { | 1115 if (m_lastTextNode && shouldEmitNewlineAfterNode(*m_node)) { |
1091 // use extra newline to represent margin bottom, as needed | 1116 // use extra newline to represent margin bottom, as needed |
1092 bool addNewline = shouldEmitExtraNewlineForNode(m_node); | 1117 bool addNewline = shouldEmitExtraNewlineForNode(m_node); |
1093 | 1118 |
1094 // FIXME: We need to emit a '\n' as we leave an empty block(s) that | 1119 // FIXME: We need to emit a '\n' as we leave an empty block(s) that |
1095 // contain a VisiblePosition when doing selection preservation. | 1120 // contain a VisiblePosition when doing selection preservation. |
1096 if (m_textState.lastCharacter() != '\n') { | 1121 if (m_textState.lastCharacter() != '\n') { |
1097 // insert a newline with a position following this block's contents. | 1122 // insert a newline with a position following this block's contents. |
1098 spliceBuffer(newlineCharacter, Strategy::parent(*baseNode), baseNode, 1, | 1123 spliceBuffer(newlineCharacter, Strategy::parent(*baseNode), baseNode, 1, |
1099 1); | 1124 1); |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1293 | 1318 |
1294 DocumentLifecycle::DisallowTransitionScope disallowTransition( | 1319 DocumentLifecycle::DisallowTransitionScope disallowTransition( |
1295 range.startPosition().document()->lifecycle()); | 1320 range.startPosition().document()->lifecycle()); |
1296 | 1321 |
1297 TextIteratorAlgorithm<Strategy> it(range.startPosition(), range.endPosition(), | 1322 TextIteratorAlgorithm<Strategy> it(range.startPosition(), range.endPosition(), |
1298 behavior); | 1323 behavior); |
1299 | 1324 |
1300 if (it.atEnd()) | 1325 if (it.atEnd()) |
1301 return emptyString(); | 1326 return emptyString(); |
1302 | 1327 |
1303 // The initial buffer size can be critical for performance: https://bugs.webki
t.org/show_bug.cgi?id=81192 | 1328 // The initial buffer size can be critical for performance: |
| 1329 // https://bugs.webkit.org/show_bug.cgi?id=81192 |
1304 static const unsigned initialCapacity = 1 << 15; | 1330 static const unsigned initialCapacity = 1 << 15; |
1305 | 1331 |
1306 StringBuilder builder; | 1332 StringBuilder builder; |
1307 builder.reserveCapacity(initialCapacity); | 1333 builder.reserveCapacity(initialCapacity); |
1308 | 1334 |
1309 for (; !it.atEnd(); it.advance()) | 1335 for (; !it.atEnd(); it.advance()) |
1310 it.text().appendTextToStringBuilder(builder); | 1336 it.text().appendTextToStringBuilder(builder); |
1311 | 1337 |
1312 if (builder.isEmpty()) | 1338 if (builder.isEmpty()) |
1313 return emptyString(); | 1339 return emptyString(); |
1314 | 1340 |
1315 return builder.toString(); | 1341 return builder.toString(); |
1316 } | 1342 } |
1317 | 1343 |
1318 String plainText(const EphemeralRange& range, | 1344 String plainText(const EphemeralRange& range, |
1319 TextIteratorBehaviorFlags behavior) { | 1345 TextIteratorBehaviorFlags behavior) { |
1320 return createPlainText<EditingStrategy>(range, behavior); | 1346 return createPlainText<EditingStrategy>(range, behavior); |
1321 } | 1347 } |
1322 | 1348 |
1323 String plainText(const EphemeralRangeInFlatTree& range, | 1349 String plainText(const EphemeralRangeInFlatTree& range, |
1324 TextIteratorBehaviorFlags behavior) { | 1350 TextIteratorBehaviorFlags behavior) { |
1325 return createPlainText<EditingInFlatTreeStrategy>(range, behavior); | 1351 return createPlainText<EditingInFlatTreeStrategy>(range, behavior); |
1326 } | 1352 } |
1327 | 1353 |
1328 template class CORE_TEMPLATE_EXPORT TextIteratorAlgorithm<EditingStrategy>; | 1354 template class CORE_TEMPLATE_EXPORT TextIteratorAlgorithm<EditingStrategy>; |
1329 template class CORE_TEMPLATE_EXPORT | 1355 template class CORE_TEMPLATE_EXPORT |
1330 TextIteratorAlgorithm<EditingInFlatTreeStrategy>; | 1356 TextIteratorAlgorithm<EditingInFlatTreeStrategy>; |
1331 | 1357 |
1332 } // namespace blink | 1358 } // namespace blink |
OLD | NEW |