| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. |
| 3 * | 3 * |
| 4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
| 5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
| 6 * are met: | 6 * are met: |
| 7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
| 8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
| 10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 259 end = swap; | 259 end = swap; |
| 260 } | 260 } |
| 261 | 261 |
| 262 VisiblePosition visibleStart = createVisiblePositionDeprecated(start); | 262 VisiblePosition visibleStart = createVisiblePositionDeprecated(start); |
| 263 VisiblePosition visibleEnd = createVisiblePositionDeprecated(end); | 263 VisiblePosition visibleEnd = createVisiblePositionDeprecated(end); |
| 264 | 264 |
| 265 if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || | 265 if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || |
| 266 visibleEnd.isOrphan()) | 266 visibleEnd.isOrphan()) |
| 267 return; | 267 return; |
| 268 | 268 |
| 269 // Save and restore the selection endpoints using their indices in the documen
t, since | 269 // Save and restore the selection endpoints using their indices in the |
| 270 // addBlockStyleIfNeeded may moveParagraphs, which can remove these endpoints. | 270 // document, since addBlockStyleIfNeeded may moveParagraphs, which can remove |
| 271 // Calculate start and end indices from the start of the tree that they're in. | 271 // these endpoints. Calculate start and end indices from the start of the tree |
| 272 // that they're in. |
| 272 Node& scope = NodeTraversal::highestAncestorOrSelf( | 273 Node& scope = NodeTraversal::highestAncestorOrSelf( |
| 273 *visibleStart.deepEquivalent().anchorNode()); | 274 *visibleStart.deepEquivalent().anchorNode()); |
| 274 Range* startRange = | 275 Range* startRange = |
| 275 Range::create(document(), Position::firstPositionInNode(&scope), | 276 Range::create(document(), Position::firstPositionInNode(&scope), |
| 276 visibleStart.deepEquivalent().parentAnchoredEquivalent()); | 277 visibleStart.deepEquivalent().parentAnchoredEquivalent()); |
| 277 Range* endRange = | 278 Range* endRange = |
| 278 Range::create(document(), Position::firstPositionInNode(&scope), | 279 Range::create(document(), Position::firstPositionInNode(&scope), |
| 279 visibleEnd.deepEquivalent().parentAnchoredEquivalent()); | 280 visibleEnd.deepEquivalent().parentAnchoredEquivalent()); |
| 280 int startIndex = TextIterator::rangeLength(startRange->startPosition(), | 281 int startIndex = TextIterator::rangeLength(startRange->startPosition(), |
| 281 startRange->endPosition(), true); | 282 startRange->endPosition(), true); |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 401 | 402 |
| 402 if (isValidCaretPositionInTextNode(end)) { | 403 if (isValidCaretPositionInTextNode(end)) { |
| 403 splitTextAtEnd(start, end); | 404 splitTextAtEnd(start, end); |
| 404 start = startPosition(); | 405 start = startPosition(); |
| 405 end = endPosition(); | 406 end = endPosition(); |
| 406 } | 407 } |
| 407 | 408 |
| 408 DCHECK(start.anchorNode()); | 409 DCHECK(start.anchorNode()); |
| 409 DCHECK(end.anchorNode()); | 410 DCHECK(end.anchorNode()); |
| 410 // Calculate loop end point. | 411 // Calculate loop end point. |
| 411 // If the end node is before the start node (can only happen if the end node i
s | 412 // If the end node is before the start node (can only happen if the end node |
| 412 // an ancestor of the start node), we gather nodes up to the next sibling of t
he end node | 413 // is an ancestor of the start node), we gather nodes up to the next sibling |
| 414 // of the end node |
| 413 const Node* const beyondEnd = end.nodeAsRangePastLastNode(); | 415 const Node* const beyondEnd = end.nodeAsRangePastLastNode(); |
| 414 start = mostBackwardCaretPosition( | 416 // Move upstream to ensure we do not add redundant spans. |
| 415 start); // Move upstream to ensure we do not add redundant spans. | 417 start = mostBackwardCaretPosition(start); |
| 416 Node* startNode = start.anchorNode(); | 418 Node* startNode = start.anchorNode(); |
| 417 DCHECK(startNode); | 419 DCHECK(startNode); |
| 418 | 420 |
| 419 // Make sure we're not already at the end or the next NodeTraversal::next() wi
ll traverse | 421 // Make sure we're not already at the end or the next NodeTraversal::next() |
| 420 // past it. | 422 // will traverse past it. |
| 421 if (startNode == beyondEnd) | 423 if (startNode == beyondEnd) |
| 422 return; | 424 return; |
| 423 | 425 |
| 424 if (startNode->isTextNode() && | 426 if (startNode->isTextNode() && |
| 425 start.computeOffsetInContainerNode() >= caretMaxOffset(startNode)) { | 427 start.computeOffsetInContainerNode() >= caretMaxOffset(startNode)) { |
| 426 // Move out of text node if range does not include its characters. | 428 // Move out of text node if range does not include its characters. |
| 427 startNode = NodeTraversal::next(*startNode); | 429 startNode = NodeTraversal::next(*startNode); |
| 428 if (!startNode) | 430 if (!startNode) |
| 429 return; | 431 return; |
| 430 } | 432 } |
| 431 | 433 |
| 432 // Store away font size before making any changes to the document. | 434 // Store away font size before making any changes to the document. |
| 433 // This ensures that changes to one node won't effect another. | 435 // This ensures that changes to one node won't effect another. |
| 434 HeapHashMap<Member<Node>, float> startingFontSizes; | 436 HeapHashMap<Member<Node>, float> startingFontSizes; |
| 435 for (Node* node = startNode; node != beyondEnd; | 437 for (Node* node = startNode; node != beyondEnd; |
| 436 node = NodeTraversal::next(*node)) { | 438 node = NodeTraversal::next(*node)) { |
| 437 DCHECK(node); | 439 DCHECK(node); |
| 438 startingFontSizes.set(node, computedFontSize(node)); | 440 startingFontSizes.set(node, computedFontSize(node)); |
| 439 } | 441 } |
| 440 | 442 |
| 441 // These spans were added by us. If empty after font size changes, they can be
removed. | 443 // These spans were added by us. If empty after font size changes, they can be |
| 444 // removed. |
| 442 HeapVector<Member<HTMLElement>> unstyledSpans; | 445 HeapVector<Member<HTMLElement>> unstyledSpans; |
| 443 | 446 |
| 444 Node* lastStyledNode = nullptr; | 447 Node* lastStyledNode = nullptr; |
| 445 for (Node* node = startNode; node != beyondEnd; | 448 for (Node* node = startNode; node != beyondEnd; |
| 446 node = NodeTraversal::next(*node)) { | 449 node = NodeTraversal::next(*node)) { |
| 447 DCHECK(node); | 450 DCHECK(node); |
| 448 HTMLElement* element = nullptr; | 451 HTMLElement* element = nullptr; |
| 449 if (node->isHTMLElement()) { | 452 if (node->isHTMLElement()) { |
| 450 // Only work on fully selected nodes. | 453 // Only work on fully selected nodes. |
| 451 if (!elementFullySelected(toHTMLElement(*node), start, end)) | 454 if (!elementFullySelected(toHTMLElement(*node), start, end)) |
| 452 continue; | 455 continue; |
| 453 element = toHTMLElement(node); | 456 element = toHTMLElement(node); |
| 454 } else if (node->isTextNode() && node->layoutObject() && | 457 } else if (node->isTextNode() && node->layoutObject() && |
| 455 node->parentNode() != lastStyledNode) { | 458 node->parentNode() != lastStyledNode) { |
| 456 // Last styled node was not parent node of this text node, but we wish to
style this | 459 // Last styled node was not parent node of this text node, but we wish to |
| 457 // text node. To make this possible, add a style span to surround this tex
t node. | 460 // style this text node. To make this possible, add a style span to |
| 461 // surround this text node. |
| 458 HTMLSpanElement* span = HTMLSpanElement::create(document()); | 462 HTMLSpanElement* span = HTMLSpanElement::create(document()); |
| 459 surroundNodeRangeWithElement(node, node, span, editingState); | 463 surroundNodeRangeWithElement(node, node, span, editingState); |
| 460 if (editingState->isAborted()) | 464 if (editingState->isAborted()) |
| 461 return; | 465 return; |
| 462 element = span; | 466 element = span; |
| 463 } else { | 467 } else { |
| 464 // Only handle HTML elements and text nodes. | 468 // Only handle HTML elements and text nodes. |
| 465 continue; | 469 continue; |
| 466 } | 470 } |
| 467 lastStyledNode = node; | 471 lastStyledNode = node; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 525 if (editingState->isAborted()) | 529 if (editingState->isAborted()) |
| 526 return; | 530 return; |
| 527 } | 531 } |
| 528 } | 532 } |
| 529 } | 533 } |
| 530 | 534 |
| 531 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi( | 535 HTMLElement* ApplyStyleCommand::splitAncestorsWithUnicodeBidi( |
| 532 Node* node, | 536 Node* node, |
| 533 bool before, | 537 bool before, |
| 534 WritingDirection allowedDirection) { | 538 WritingDirection allowedDirection) { |
| 535 // We are allowed to leave the highest ancestor with unicode-bidi unsplit if i
t is unicode-bidi: embed and direction: allowedDirection. | 539 // We are allowed to leave the highest ancestor with unicode-bidi unsplit if |
| 536 // In that case, we return the unsplit ancestor. Otherwise, we return 0. | 540 // it is unicode-bidi: embed and direction: allowedDirection. In that case, we |
| 541 // return the unsplit ancestor. Otherwise, we return 0. |
| 537 Element* block = enclosingBlock(node); | 542 Element* block = enclosingBlock(node); |
| 538 if (!block) | 543 if (!block) |
| 539 return 0; | 544 return 0; |
| 540 | 545 |
| 541 ContainerNode* highestAncestorWithUnicodeBidi = nullptr; | 546 ContainerNode* highestAncestorWithUnicodeBidi = nullptr; |
| 542 ContainerNode* nextHighestAncestorWithUnicodeBidi = nullptr; | 547 ContainerNode* nextHighestAncestorWithUnicodeBidi = nullptr; |
| 543 int highestAncestorUnicodeBidi = 0; | 548 int highestAncestorUnicodeBidi = 0; |
| 544 for (Node& runner : NodeTraversal::ancestorsOf(*node)) { | 549 for (Node& runner : NodeTraversal::ancestorsOf(*node)) { |
| 545 if (runner == block) | 550 if (runner == block) |
| 546 break; | 551 break; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 break; | 604 break; |
| 600 if (!runner.isStyledElement()) | 605 if (!runner.isStyledElement()) |
| 601 continue; | 606 continue; |
| 602 | 607 |
| 603 Element* element = toElement(&runner); | 608 Element* element = toElement(&runner); |
| 604 int unicodeBidi = getIdentifierValue( | 609 int unicodeBidi = getIdentifierValue( |
| 605 CSSComputedStyleDeclaration::create(element), CSSPropertyUnicodeBidi); | 610 CSSComputedStyleDeclaration::create(element), CSSPropertyUnicodeBidi); |
| 606 if (!unicodeBidi || unicodeBidi == CSSValueNormal) | 611 if (!unicodeBidi || unicodeBidi == CSSValueNormal) |
| 607 continue; | 612 continue; |
| 608 | 613 |
| 609 // FIXME: This code should really consider the mapped attribute 'dir', the i
nline style declaration, | 614 // FIXME: This code should really consider the mapped attribute 'dir', the |
| 610 // and all matching style rules in order to determine how to best set the un
icode-bidi property to 'normal'. | 615 // inline style declaration, and all matching style rules in order to |
| 611 // For now, it assumes that if the 'dir' attribute is present, then removing
it will suffice, and | 616 // determine how to best set the unicode-bidi property to 'normal'. For now, |
| 612 // otherwise it sets the property in the inline style declaration. | 617 // it assumes that if the 'dir' attribute is present, then removing it will |
| 618 // suffice, and otherwise it sets the property in the inline style |
| 619 // declaration. |
| 613 if (element->hasAttribute(dirAttr)) { | 620 if (element->hasAttribute(dirAttr)) { |
| 614 // FIXME: If this is a BDO element, we should probably just remove it if i
t has no | 621 // FIXME: If this is a BDO element, we should probably just remove it if |
| 615 // other attributes, like we (should) do with B and I elements. | 622 // it has no other attributes, like we (should) do with B and I elements. |
| 616 removeElementAttribute(element, dirAttr); | 623 removeElementAttribute(element, dirAttr); |
| 617 } else { | 624 } else { |
| 618 MutableStylePropertySet* inlineStyle = | 625 MutableStylePropertySet* inlineStyle = |
| 619 copyStyleOrCreateEmpty(element->inlineStyle()); | 626 copyStyleOrCreateEmpty(element->inlineStyle()); |
| 620 inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal); | 627 inlineStyle->setProperty(CSSPropertyUnicodeBidi, CSSValueNormal); |
| 621 inlineStyle->removeProperty(CSSPropertyDirection); | 628 inlineStyle->removeProperty(CSSPropertyDirection); |
| 622 setNodeAttribute(element, styleAttr, AtomicString(inlineStyle->asText())); | 629 setNodeAttribute(element, styleAttr, AtomicString(inlineStyle->asText())); |
| 623 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) { | 630 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) { |
| 624 removeNodePreservingChildren(element, editingState); | 631 removeNodePreservingChildren(element, editingState); |
| 625 if (editingState->isAborted()) | 632 if (editingState->isAborted()) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 658 | 665 |
| 659 if (start.isNull() || end.isNull()) | 666 if (start.isNull() || end.isNull()) |
| 660 return; | 667 return; |
| 661 | 668 |
| 662 if (comparePositions(end, start) < 0) { | 669 if (comparePositions(end, start) < 0) { |
| 663 Position swap = start; | 670 Position swap = start; |
| 664 start = end; | 671 start = end; |
| 665 end = swap; | 672 end = swap; |
| 666 } | 673 } |
| 667 | 674 |
| 668 // split the start node and containing element if the selection starts inside
of it | 675 // split the start node and containing element if the selection starts inside |
| 676 // of it |
| 669 bool splitStart = isValidCaretPositionInTextNode(start); | 677 bool splitStart = isValidCaretPositionInTextNode(start); |
| 670 if (splitStart) { | 678 if (splitStart) { |
| 671 if (shouldSplitTextElement(start.anchorNode()->parentElement(), style)) | 679 if (shouldSplitTextElement(start.anchorNode()->parentElement(), style)) |
| 672 splitTextElementAtStart(start, end); | 680 splitTextElementAtStart(start, end); |
| 673 else | 681 else |
| 674 splitTextAtStart(start, end); | 682 splitTextAtStart(start, end); |
| 675 start = startPosition(); | 683 start = startPosition(); |
| 676 end = endPosition(); | 684 end = endPosition(); |
| 677 if (start.isNull() || end.isNull()) | 685 if (start.isNull() || end.isNull()) |
| 678 return; | 686 return; |
| 679 startDummySpanAncestor = dummySpanAncestorForNode(start.anchorNode()); | 687 startDummySpanAncestor = dummySpanAncestorForNode(start.anchorNode()); |
| 680 } | 688 } |
| 681 | 689 |
| 682 // split the end node and containing element if the selection ends inside of i
t | 690 // split the end node and containing element if the selection ends inside of |
| 691 // it |
| 683 bool splitEnd = isValidCaretPositionInTextNode(end); | 692 bool splitEnd = isValidCaretPositionInTextNode(end); |
| 684 if (splitEnd) { | 693 if (splitEnd) { |
| 685 if (shouldSplitTextElement(end.anchorNode()->parentElement(), style)) | 694 if (shouldSplitTextElement(end.anchorNode()->parentElement(), style)) |
| 686 splitTextElementAtEnd(start, end); | 695 splitTextElementAtEnd(start, end); |
| 687 else | 696 else |
| 688 splitTextAtEnd(start, end); | 697 splitTextAtEnd(start, end); |
| 689 start = startPosition(); | 698 start = startPosition(); |
| 690 end = endPosition(); | 699 end = endPosition(); |
| 691 if (start.isNull() || end.isNull()) | 700 if (start.isNull() || end.isNull()) |
| 692 return; | 701 return; |
| 693 endDummySpanAncestor = dummySpanAncestorForNode(end.anchorNode()); | 702 endDummySpanAncestor = dummySpanAncestorForNode(end.anchorNode()); |
| 694 } | 703 } |
| 695 | 704 |
| 696 // Remove style from the selection. | 705 // Remove style from the selection. |
| 697 // Use the upstream position of the start for removing style. | 706 // Use the upstream position of the start for removing style. |
| 698 // This will ensure we remove all traces of the relevant styles from the selec
tion | 707 // This will ensure we remove all traces of the relevant styles from the |
| 699 // and prevent us from adding redundant ones, as described in: | 708 // selection and prevent us from adding redundant ones, as described in: |
| 700 // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags | 709 // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags |
| 701 Position removeStart = mostBackwardCaretPosition(start); | 710 Position removeStart = mostBackwardCaretPosition(start); |
| 702 WritingDirection textDirection = NaturalWritingDirection; | 711 WritingDirection textDirection = NaturalWritingDirection; |
| 703 bool hasTextDirection = style->textDirection(textDirection); | 712 bool hasTextDirection = style->textDirection(textDirection); |
| 704 EditingStyle* styleWithoutEmbedding = nullptr; | 713 EditingStyle* styleWithoutEmbedding = nullptr; |
| 705 EditingStyle* embeddingStyle = nullptr; | 714 EditingStyle* embeddingStyle = nullptr; |
| 706 if (hasTextDirection) { | 715 if (hasTextDirection) { |
| 707 // Leave alone an ancestor that provides the desired single level embedding,
if there is one. | 716 // Leave alone an ancestor that provides the desired single level embedding, |
| 717 // if there is one. |
| 708 HTMLElement* startUnsplitAncestor = | 718 HTMLElement* startUnsplitAncestor = |
| 709 splitAncestorsWithUnicodeBidi(start.anchorNode(), true, textDirection); | 719 splitAncestorsWithUnicodeBidi(start.anchorNode(), true, textDirection); |
| 710 HTMLElement* endUnsplitAncestor = | 720 HTMLElement* endUnsplitAncestor = |
| 711 splitAncestorsWithUnicodeBidi(end.anchorNode(), false, textDirection); | 721 splitAncestorsWithUnicodeBidi(end.anchorNode(), false, textDirection); |
| 712 removeEmbeddingUpToEnclosingBlock(start.anchorNode(), startUnsplitAncestor, | 722 removeEmbeddingUpToEnclosingBlock(start.anchorNode(), startUnsplitAncestor, |
| 713 editingState); | 723 editingState); |
| 714 if (editingState->isAborted()) | 724 if (editingState->isAborted()) |
| 715 return; | 725 return; |
| 716 removeEmbeddingUpToEnclosingBlock(end.anchorNode(), endUnsplitAncestor, | 726 removeEmbeddingUpToEnclosingBlock(end.anchorNode(), endUnsplitAncestor, |
| 717 editingState); | 727 editingState); |
| 718 if (editingState->isAborted()) | 728 if (editingState->isAborted()) |
| 719 return; | 729 return; |
| 720 | 730 |
| 721 // Avoid removing the dir attribute and the unicode-bidi and direction prope
rties from the unsplit ancestors. | 731 // Avoid removing the dir attribute and the unicode-bidi and direction |
| 732 // properties from the unsplit ancestors. |
| 722 Position embeddingRemoveStart = removeStart; | 733 Position embeddingRemoveStart = removeStart; |
| 723 if (startUnsplitAncestor && | 734 if (startUnsplitAncestor && |
| 724 elementFullySelected(*startUnsplitAncestor, removeStart, end)) | 735 elementFullySelected(*startUnsplitAncestor, removeStart, end)) |
| 725 embeddingRemoveStart = Position::inParentAfterNode(*startUnsplitAncestor); | 736 embeddingRemoveStart = Position::inParentAfterNode(*startUnsplitAncestor); |
| 726 | 737 |
| 727 Position embeddingRemoveEnd = end; | 738 Position embeddingRemoveEnd = end; |
| 728 if (endUnsplitAncestor && | 739 if (endUnsplitAncestor && |
| 729 elementFullySelected(*endUnsplitAncestor, removeStart, end)) | 740 elementFullySelected(*endUnsplitAncestor, removeStart, end)) |
| 730 embeddingRemoveEnd = mostForwardCaretPosition( | 741 embeddingRemoveEnd = mostForwardCaretPosition( |
| 731 Position::inParentBeforeNode(*endUnsplitAncestor)); | 742 Position::inParentBeforeNode(*endUnsplitAncestor)); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 771 end = endPosition(); | 782 end = endPosition(); |
| 772 } | 783 } |
| 773 | 784 |
| 774 // update document layout once before running the rest of the function | 785 // update document layout once before running the rest of the function |
| 775 // so that we avoid the expense of updating before each and every call | 786 // so that we avoid the expense of updating before each and every call |
| 776 // to check a computed style | 787 // to check a computed style |
| 777 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 788 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 778 | 789 |
| 779 EditingStyle* styleToApply = style; | 790 EditingStyle* styleToApply = style; |
| 780 if (hasTextDirection) { | 791 if (hasTextDirection) { |
| 781 // Avoid applying the unicode-bidi and direction properties beneath ancestor
s that already have them. | 792 // Avoid applying the unicode-bidi and direction properties beneath |
| 793 // ancestors that already have them. |
| 782 HTMLElement* embeddingStartElement = highestEmbeddingAncestor( | 794 HTMLElement* embeddingStartElement = highestEmbeddingAncestor( |
| 783 start.anchorNode(), enclosingBlock(start.anchorNode())); | 795 start.anchorNode(), enclosingBlock(start.anchorNode())); |
| 784 HTMLElement* embeddingEndElement = highestEmbeddingAncestor( | 796 HTMLElement* embeddingEndElement = highestEmbeddingAncestor( |
| 785 end.anchorNode(), enclosingBlock(end.anchorNode())); | 797 end.anchorNode(), enclosingBlock(end.anchorNode())); |
| 786 | 798 |
| 787 if (embeddingStartElement || embeddingEndElement) { | 799 if (embeddingStartElement || embeddingEndElement) { |
| 788 Position embeddingApplyStart = | 800 Position embeddingApplyStart = |
| 789 embeddingStartElement | 801 embeddingStartElement |
| 790 ? Position::inParentAfterNode(*embeddingStartElement) | 802 ? Position::inParentAfterNode(*embeddingStartElement) |
| 791 : start; | 803 : start; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 833 startNode = NodeTraversal::next(*startNode); | 845 startNode = NodeTraversal::next(*startNode); |
| 834 if (!startNode || | 846 if (!startNode || |
| 835 comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0) | 847 comparePositions(end, firstPositionInOrBeforeNode(startNode)) < 0) |
| 836 return; | 848 return; |
| 837 } | 849 } |
| 838 | 850 |
| 839 Node* pastEndNode = end.anchorNode(); | 851 Node* pastEndNode = end.anchorNode(); |
| 840 if (end.computeEditingOffset() >= caretMaxOffset(end.anchorNode())) | 852 if (end.computeEditingOffset() >= caretMaxOffset(end.anchorNode())) |
| 841 pastEndNode = NodeTraversal::nextSkippingChildren(*end.anchorNode()); | 853 pastEndNode = NodeTraversal::nextSkippingChildren(*end.anchorNode()); |
| 842 | 854 |
| 843 // FIXME: Callers should perform this operation on a Range that includes the b
r | 855 // FIXME: Callers should perform this operation on a Range that includes the |
| 844 // if they want style applied to the empty line. | 856 // br if they want style applied to the empty line. |
| 845 if (start == end && isHTMLBRElement(*start.anchorNode())) | 857 if (start == end && isHTMLBRElement(*start.anchorNode())) |
| 846 pastEndNode = NodeTraversal::next(*start.anchorNode()); | 858 pastEndNode = NodeTraversal::next(*start.anchorNode()); |
| 847 | 859 |
| 848 // Start from the highest fully selected ancestor so that we can modify the fu
lly selected node. | 860 // Start from the highest fully selected ancestor so that we can modify the |
| 849 // e.g. When applying font-size: large on <font color="blue">hello</font>, we
need to include the font element in our run | 861 // fully selected node. e.g. When applying font-size: large on <font |
| 850 // to generate <font color="blue" size="4">hello</font> instead of <font color
="blue"><font size="4">hello</font></font> | 862 // color="blue">hello</font>, we need to include the font element in our run |
| 863 // to generate <font color="blue" size="4">hello</font> instead of <font |
| 864 // color="blue"><font size="4">hello</font></font> |
| 851 Range* range = Range::create(startNode->document(), start, end); | 865 Range* range = Range::create(startNode->document(), start, end); |
| 852 Element* editableRoot = rootEditableElement(*startNode); | 866 Element* editableRoot = rootEditableElement(*startNode); |
| 853 if (startNode != editableRoot) { | 867 if (startNode != editableRoot) { |
| 854 while (editableRoot && startNode->parentNode() != editableRoot && | 868 while (editableRoot && startNode->parentNode() != editableRoot && |
| 855 isNodeVisiblyContainedWithin(*startNode->parentNode(), *range)) | 869 isNodeVisiblyContainedWithin(*startNode->parentNode(), *range)) |
| 856 startNode = startNode->parentNode(); | 870 startNode = startNode->parentNode(); |
| 857 } | 871 } |
| 858 | 872 |
| 859 applyInlineStyleToNodeRange(style, startNode, pastEndNode, editingState); | 873 applyInlineStyleToNodeRange(style, startNode, pastEndNode, editingState); |
| 860 } | 874 } |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 923 Node* node = startNode; | 937 Node* node = startNode; |
| 924 for (Node* next; node && node != pastEndNode; node = next) { | 938 for (Node* next; node && node != pastEndNode; node = next) { |
| 925 next = NodeTraversal::next(*node); | 939 next = NodeTraversal::next(*node); |
| 926 | 940 |
| 927 if (!node->layoutObject() || !hasEditableStyle(*node)) | 941 if (!node->layoutObject() || !hasEditableStyle(*node)) |
| 928 continue; | 942 continue; |
| 929 | 943 |
| 930 if (!hasRichlyEditableStyle(*node) && node->isHTMLElement()) { | 944 if (!hasRichlyEditableStyle(*node) && node->isHTMLElement()) { |
| 931 HTMLElement* element = toHTMLElement(node); | 945 HTMLElement* element = toHTMLElement(node); |
| 932 // This is a plaintext-only region. Only proceed if it's fully selected. | 946 // This is a plaintext-only region. Only proceed if it's fully selected. |
| 933 // pastEndNode is the node after the last fully selected node, so if it's
inside node then | 947 // pastEndNode is the node after the last fully selected node, so if it's |
| 934 // node isn't fully selected. | 948 // inside node then node isn't fully selected. |
| 935 if (pastEndNode && pastEndNode->isDescendantOf(element)) | 949 if (pastEndNode && pastEndNode->isDescendantOf(element)) |
| 936 break; | 950 break; |
| 937 // Add to this element's inline style and skip over its contents. | 951 // Add to this element's inline style and skip over its contents. |
| 938 next = NodeTraversal::nextSkippingChildren(*node); | 952 next = NodeTraversal::nextSkippingChildren(*node); |
| 939 if (!style->style()) | 953 if (!style->style()) |
| 940 continue; | 954 continue; |
| 941 MutableStylePropertySet* inlineStyle = | 955 MutableStylePropertySet* inlineStyle = |
| 942 copyStyleOrCreateEmpty(element->inlineStyle()); | 956 copyStyleOrCreateEmpty(element->inlineStyle()); |
| 943 inlineStyle->mergeAndOverrideOnConflict(style->style()); | 957 inlineStyle->mergeAndOverrideOnConflict(style->style()); |
| 944 setNodeAttribute(element, styleAttr, AtomicString(inlineStyle->asText())); | 958 setNodeAttribute(element, styleAttr, AtomicString(inlineStyle->asText())); |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1023 bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, | 1037 bool ApplyStyleCommand::shouldApplyInlineStyleToRun(EditingStyle* style, |
| 1024 Node* runStart, | 1038 Node* runStart, |
| 1025 Node* pastEndNode) { | 1039 Node* pastEndNode) { |
| 1026 DCHECK(style); | 1040 DCHECK(style); |
| 1027 DCHECK(runStart); | 1041 DCHECK(runStart); |
| 1028 | 1042 |
| 1029 for (Node* node = runStart; node && node != pastEndNode; | 1043 for (Node* node = runStart; node && node != pastEndNode; |
| 1030 node = NodeTraversal::next(*node)) { | 1044 node = NodeTraversal::next(*node)) { |
| 1031 if (node->hasChildren()) | 1045 if (node->hasChildren()) |
| 1032 continue; | 1046 continue; |
| 1033 // We don't consider m_isInlineElementToRemoveFunction here because we never
apply style when m_isInlineElementToRemoveFunction is specified | 1047 // We don't consider m_isInlineElementToRemoveFunction here because we never |
| 1048 // apply style when m_isInlineElementToRemoveFunction is specified |
| 1034 if (!style->styleIsPresentInComputedStyleOfNode(node)) | 1049 if (!style->styleIsPresentInComputedStyleOfNode(node)) |
| 1035 return true; | 1050 return true; |
| 1036 if (m_styledInlineElement && | 1051 if (m_styledInlineElement && |
| 1037 !enclosingElementWithTag(Position::beforeNode(node), | 1052 !enclosingElementWithTag(Position::beforeNode(node), |
| 1038 m_styledInlineElement->tagQName())) | 1053 m_styledInlineElement->tagQName())) |
| 1039 return true; | 1054 return true; |
| 1040 } | 1055 } |
| 1041 return false; | 1056 return false; |
| 1042 } | 1057 } |
| 1043 | 1058 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1062 continue; | 1077 continue; |
| 1063 | 1078 |
| 1064 HTMLElement& element = toHTMLElement(*node); | 1079 HTMLElement& element = toHTMLElement(*node); |
| 1065 Node* previousSibling = element.previousSibling(); | 1080 Node* previousSibling = element.previousSibling(); |
| 1066 Node* nextSibling = element.nextSibling(); | 1081 Node* nextSibling = element.nextSibling(); |
| 1067 ContainerNode* parent = element.parentNode(); | 1082 ContainerNode* parent = element.parentNode(); |
| 1068 removeInlineStyleFromElement(style, &element, editingState, RemoveAlways); | 1083 removeInlineStyleFromElement(style, &element, editingState, RemoveAlways); |
| 1069 if (editingState->isAborted()) | 1084 if (editingState->isAborted()) |
| 1070 return; | 1085 return; |
| 1071 if (!element.isConnected()) { | 1086 if (!element.isConnected()) { |
| 1072 // FIXME: We might need to update the start and the end of current selecti
on here but need a test. | 1087 // FIXME: We might need to update the start and the end of current |
| 1088 // selection here but need a test. |
| 1073 if (runStart == element) | 1089 if (runStart == element) |
| 1074 runStart = previousSibling ? previousSibling->nextSibling() | 1090 runStart = previousSibling ? previousSibling->nextSibling() |
| 1075 : parent->firstChild(); | 1091 : parent->firstChild(); |
| 1076 if (runEnd == element) | 1092 if (runEnd == element) |
| 1077 runEnd = | 1093 runEnd = |
| 1078 nextSibling ? nextSibling->previousSibling() : parent->lastChild(); | 1094 nextSibling ? nextSibling->previousSibling() : parent->lastChild(); |
| 1079 } | 1095 } |
| 1080 } | 1096 } |
| 1081 } | 1097 } |
| 1082 | 1098 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1147 if (style->conflictsWithImplicitStyleOfElement( | 1163 if (style->conflictsWithImplicitStyleOfElement( |
| 1148 element, extractedStyle, | 1164 element, extractedStyle, |
| 1149 mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle | 1165 mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle |
| 1150 : EditingStyle::DoNotExtractMatchingStyle)) { | 1166 : EditingStyle::DoNotExtractMatchingStyle)) { |
| 1151 replaceWithSpanOrRemoveIfWithoutAttributes(element, editingState); | 1167 replaceWithSpanOrRemoveIfWithoutAttributes(element, editingState); |
| 1152 if (editingState->isAborted()) | 1168 if (editingState->isAborted()) |
| 1153 return false; | 1169 return false; |
| 1154 return true; | 1170 return true; |
| 1155 } | 1171 } |
| 1156 | 1172 |
| 1157 // unicode-bidi and direction are pushed down separately so don't push down wi
th other styles | 1173 // unicode-bidi and direction are pushed down separately so don't push down |
| 1174 // with other styles |
| 1158 Vector<QualifiedName> attributes; | 1175 Vector<QualifiedName> attributes; |
| 1159 if (!style->extractConflictingImplicitStyleOfAttributes( | 1176 if (!style->extractConflictingImplicitStyleOfAttributes( |
| 1160 element, extractedStyle ? EditingStyle::PreserveWritingDirection | 1177 element, extractedStyle ? EditingStyle::PreserveWritingDirection |
| 1161 : EditingStyle::DoNotPreserveWritingDirection, | 1178 : EditingStyle::DoNotPreserveWritingDirection, |
| 1162 extractedStyle, attributes, | 1179 extractedStyle, attributes, |
| 1163 mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle | 1180 mode == RemoveAlways ? EditingStyle::ExtractMatchingStyle |
| 1164 : EditingStyle::DoNotExtractMatchingStyle)) | 1181 : EditingStyle::DoNotExtractMatchingStyle)) |
| 1165 return false; | 1182 return false; |
| 1166 | 1183 |
| 1167 for (const auto& attribute : attributes) | 1184 for (const auto& attribute : attributes) |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1186 DCHECK(element); | 1203 DCHECK(element); |
| 1187 | 1204 |
| 1188 if (mode == RemoveNone) | 1205 if (mode == RemoveNone) |
| 1189 return style->conflictsWithInlineStyleOfElement(element); | 1206 return style->conflictsWithInlineStyleOfElement(element); |
| 1190 | 1207 |
| 1191 Vector<CSSPropertyID> properties; | 1208 Vector<CSSPropertyID> properties; |
| 1192 if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, | 1209 if (!style->conflictsWithInlineStyleOfElement(element, extractedStyle, |
| 1193 properties)) | 1210 properties)) |
| 1194 return false; | 1211 return false; |
| 1195 | 1212 |
| 1196 // FIXME: We should use a mass-removal function here but we don't have an undo
able one yet. | 1213 // FIXME: We should use a mass-removal function here but we don't have an |
| 1214 // undoable one yet. |
| 1197 for (const auto& property : properties) | 1215 for (const auto& property : properties) |
| 1198 removeCSSProperty(element, property); | 1216 removeCSSProperty(element, property); |
| 1199 | 1217 |
| 1200 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) | 1218 if (isSpanWithoutAttributesOrUnstyledStyleSpan(element)) |
| 1201 removeNodePreservingChildren(element, editingState); | 1219 removeNodePreservingChildren(element, editingState); |
| 1202 | 1220 |
| 1203 return true; | 1221 return true; |
| 1204 } | 1222 } |
| 1205 | 1223 |
| 1206 // Finds the enclosing element until which the tree can be split. | 1224 // Finds the enclosing element until which the tree can be split. |
| 1207 // When a user hits ENTER, they won't expect this element to be split into two. | 1225 // When a user hits ENTER, they won't expect this element to be split into two. |
| 1208 // You may pass it as the second argument of splitTreeToNode. | 1226 // You may pass it as the second argument of splitTreeToNode. |
| 1209 static Element* unsplittableElementForPosition(const Position& p) { | 1227 static Element* unsplittableElementForPosition(const Position& p) { |
| 1210 // Since enclosingNodeOfType won't search beyond the highest root editable nod
e, | 1228 // Since enclosingNodeOfType won't search beyond the highest root editable |
| 1211 // this code works even if the closest table cell was outside of the root edit
able node. | 1229 // node, this code works even if the closest table cell was outside of the |
| 1230 // root editable node. |
| 1212 Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell)); | 1231 Element* enclosingCell = toElement(enclosingNodeOfType(p, &isTableCell)); |
| 1213 if (enclosingCell) | 1232 if (enclosingCell) |
| 1214 return enclosingCell; | 1233 return enclosingCell; |
| 1215 | 1234 |
| 1216 return rootEditableElementOf(p); | 1235 return rootEditableElementOf(p); |
| 1217 } | 1236 } |
| 1218 | 1237 |
| 1219 HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle( | 1238 HTMLElement* ApplyStyleCommand::highestAncestorWithConflictingInlineStyle( |
| 1220 EditingStyle* style, | 1239 EditingStyle* style, |
| 1221 Node* node) { | 1240 Node* node) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 1250 isHTMLIFrameElement(*node)) | 1269 isHTMLIFrameElement(*node)) |
| 1251 return; | 1270 return; |
| 1252 | 1271 |
| 1253 EditingStyle* newInlineStyle = style; | 1272 EditingStyle* newInlineStyle = style; |
| 1254 if (node->isHTMLElement() && toHTMLElement(node)->inlineStyle()) { | 1273 if (node->isHTMLElement() && toHTMLElement(node)->inlineStyle()) { |
| 1255 newInlineStyle = style->copy(); | 1274 newInlineStyle = style->copy(); |
| 1256 newInlineStyle->mergeInlineStyleOfElement(toHTMLElement(node), | 1275 newInlineStyle->mergeInlineStyleOfElement(toHTMLElement(node), |
| 1257 EditingStyle::OverrideValues); | 1276 EditingStyle::OverrideValues); |
| 1258 } | 1277 } |
| 1259 | 1278 |
| 1260 // Since addInlineStyleIfNeeded can't add styles to block-flow layout objects,
add style attribute instead. | 1279 // Since addInlineStyleIfNeeded can't add styles to block-flow layout objects, |
| 1280 // add style attribute instead. |
| 1261 // FIXME: applyInlineStyleToRange should be used here instead. | 1281 // FIXME: applyInlineStyleToRange should be used here instead. |
| 1262 if ((node->layoutObject()->isLayoutBlockFlow() || node->hasChildren()) && | 1282 if ((node->layoutObject()->isLayoutBlockFlow() || node->hasChildren()) && |
| 1263 node->isHTMLElement()) { | 1283 node->isHTMLElement()) { |
| 1264 setNodeAttribute(toHTMLElement(node), styleAttr, | 1284 setNodeAttribute(toHTMLElement(node), styleAttr, |
| 1265 AtomicString(newInlineStyle->style()->asText())); | 1285 AtomicString(newInlineStyle->style()->asText())); |
| 1266 return; | 1286 return; |
| 1267 } | 1287 } |
| 1268 | 1288 |
| 1269 if (node->layoutObject()->isText() && | 1289 if (node->layoutObject()->isText() && |
| 1270 toLayoutText(node->layoutObject())->isAllCollapsibleWhitespace()) | 1290 toLayoutText(node->layoutObject())->isAllCollapsibleWhitespace()) |
| 1271 return; | 1291 return; |
| 1272 | 1292 |
| 1273 // We can't wrap node with the styled element here because new styled element
will never be removed if we did. | 1293 // We can't wrap node with the styled element here because new styled element |
| 1274 // If we modified the child pointer in pushDownInlineStyleAroundNode to point
to new style element | 1294 // will never be removed if we did. If we modified the child pointer in |
| 1275 // then we fall into an infinite loop where we keep removing and adding styled
element wrapping node. | 1295 // pushDownInlineStyleAroundNode to point to new style element then we fall |
| 1296 // into an infinite loop where we keep removing and adding styled element |
| 1297 // wrapping node. |
| 1276 addInlineStyleIfNeeded(newInlineStyle, node, node, editingState); | 1298 addInlineStyleIfNeeded(newInlineStyle, node, node, editingState); |
| 1277 } | 1299 } |
| 1278 | 1300 |
| 1279 void ApplyStyleCommand::pushDownInlineStyleAroundNode( | 1301 void ApplyStyleCommand::pushDownInlineStyleAroundNode( |
| 1280 EditingStyle* style, | 1302 EditingStyle* style, |
| 1281 Node* targetNode, | 1303 Node* targetNode, |
| 1282 EditingState* editingState) { | 1304 EditingState* editingState) { |
| 1283 HTMLElement* highestAncestor = | 1305 HTMLElement* highestAncestor = |
| 1284 highestAncestorWithConflictingInlineStyle(style, targetNode); | 1306 highestAncestorWithConflictingInlineStyle(style, targetNode); |
| 1285 if (!highestAncestor) | 1307 if (!highestAncestor) |
| 1286 return; | 1308 return; |
| 1287 | 1309 |
| 1288 // The outer loop is traversing the tree vertically from highestAncestor to ta
rgetNode | 1310 // The outer loop is traversing the tree vertically from highestAncestor to |
| 1311 // targetNode |
| 1289 Node* current = highestAncestor; | 1312 Node* current = highestAncestor; |
| 1290 // Along the way, styled elements that contain targetNode are removed and accu
mulated into elementsToPushDown. | 1313 // Along the way, styled elements that contain targetNode are removed and |
| 1291 // Each child of the removed element, exclusing ancestors of targetNode, is th
en wrapped by clones of elements in elementsToPushDown. | 1314 // accumulated into elementsToPushDown. Each child of the removed element, |
| 1315 // exclusing ancestors of targetNode, is then wrapped by clones of elements in |
| 1316 // elementsToPushDown. |
| 1292 HeapVector<Member<Element>> elementsToPushDown; | 1317 HeapVector<Member<Element>> elementsToPushDown; |
| 1293 while (current && current != targetNode && current->contains(targetNode)) { | 1318 while (current && current != targetNode && current->contains(targetNode)) { |
| 1294 NodeVector currentChildren; | 1319 NodeVector currentChildren; |
| 1295 getChildNodes(toContainerNode(*current), currentChildren); | 1320 getChildNodes(toContainerNode(*current), currentChildren); |
| 1296 Element* styledElement = nullptr; | 1321 Element* styledElement = nullptr; |
| 1297 if (current->isStyledElement() && | 1322 if (current->isStyledElement() && |
| 1298 isStyledInlineElementToRemove(toElement(current))) { | 1323 isStyledInlineElementToRemove(toElement(current))) { |
| 1299 styledElement = toElement(current); | 1324 styledElement = toElement(current); |
| 1300 elementsToPushDown.append(styledElement); | 1325 elementsToPushDown.append(styledElement); |
| 1301 } | 1326 } |
| 1302 | 1327 |
| 1303 EditingStyle* styleToPushDown = EditingStyle::create(); | 1328 EditingStyle* styleToPushDown = EditingStyle::create(); |
| 1304 if (current->isHTMLElement()) { | 1329 if (current->isHTMLElement()) { |
| 1305 removeInlineStyleFromElement(style, toHTMLElement(current), editingState, | 1330 removeInlineStyleFromElement(style, toHTMLElement(current), editingState, |
| 1306 RemoveIfNeeded, styleToPushDown); | 1331 RemoveIfNeeded, styleToPushDown); |
| 1307 if (editingState->isAborted()) | 1332 if (editingState->isAborted()) |
| 1308 return; | 1333 return; |
| 1309 } | 1334 } |
| 1310 | 1335 |
| 1311 // The inner loop will go through children on each level | 1336 // The inner loop will go through children on each level |
| 1312 // FIXME: we should aggregate inline child elements together so that we don'
t wrap each child separately. | 1337 // FIXME: we should aggregate inline child elements together so that we |
| 1338 // don't wrap each child separately. |
| 1313 for (const auto& currentChild : currentChildren) { | 1339 for (const auto& currentChild : currentChildren) { |
| 1314 Node* child = currentChild; | 1340 Node* child = currentChild; |
| 1315 if (!child->parentNode()) | 1341 if (!child->parentNode()) |
| 1316 continue; | 1342 continue; |
| 1317 if (!child->contains(targetNode) && elementsToPushDown.size()) { | 1343 if (!child->contains(targetNode) && elementsToPushDown.size()) { |
| 1318 for (const auto& element : elementsToPushDown) { | 1344 for (const auto& element : elementsToPushDown) { |
| 1319 Element* wrapper = element->cloneElementWithoutChildren(); | 1345 Element* wrapper = element->cloneElementWithoutChildren(); |
| 1320 wrapper->removeAttribute(styleAttr); | 1346 wrapper->removeAttribute(styleAttr); |
| 1321 // Delete id attribute from the second element because the same id can
not be used for more than one element | 1347 // Delete id attribute from the second element because the same id |
| 1348 // cannot be used for more than one element |
| 1322 element->removeAttribute(HTMLNames::idAttr); | 1349 element->removeAttribute(HTMLNames::idAttr); |
| 1323 if (isHTMLAnchorElement(element)) | 1350 if (isHTMLAnchorElement(element)) |
| 1324 element->removeAttribute(HTMLNames::nameAttr); | 1351 element->removeAttribute(HTMLNames::nameAttr); |
| 1325 surroundNodeRangeWithElement(child, child, wrapper, editingState); | 1352 surroundNodeRangeWithElement(child, child, wrapper, editingState); |
| 1326 if (editingState->isAborted()) | 1353 if (editingState->isAborted()) |
| 1327 return; | 1354 return; |
| 1328 } | 1355 } |
| 1329 } | 1356 } |
| 1330 | 1357 |
| 1331 // Apply style to all nodes containing targetNode and their siblings but N
OT to targetNode | 1358 // Apply style to all nodes containing targetNode and their siblings but |
| 1332 // But if we've removed styledElement then go ahead and always apply the s
tyle. | 1359 // NOT to targetNode But if we've removed styledElement then go ahead and |
| 1360 // always apply the style. |
| 1333 if (child != targetNode || styledElement) { | 1361 if (child != targetNode || styledElement) { |
| 1334 applyInlineStyleToPushDown(child, styleToPushDown, editingState); | 1362 applyInlineStyleToPushDown(child, styleToPushDown, editingState); |
| 1335 if (editingState->isAborted()) | 1363 if (editingState->isAborted()) |
| 1336 return; | 1364 return; |
| 1337 } | 1365 } |
| 1338 | 1366 |
| 1339 // We found the next node for the outer loop (contains targetNode) | 1367 // We found the next node for the outer loop (contains targetNode) |
| 1340 // When reached targetNode, stop the outer loop upon the completion of the
current inner loop | 1368 // When reached targetNode, stop the outer loop upon the completion of the |
| 1369 // current inner loop |
| 1341 if (child == targetNode || child->contains(targetNode)) | 1370 if (child == targetNode || child->contains(targetNode)) |
| 1342 current = child; | 1371 current = child; |
| 1343 } | 1372 } |
| 1344 } | 1373 } |
| 1345 } | 1374 } |
| 1346 | 1375 |
| 1347 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, | 1376 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, |
| 1348 const Position& start, | 1377 const Position& start, |
| 1349 const Position& end, | 1378 const Position& end, |
| 1350 EditingState* editingState) { | 1379 EditingState* editingState) { |
| 1351 DCHECK(start.isNotNull()); | 1380 DCHECK(start.isNotNull()); |
| 1352 DCHECK(end.isNotNull()); | 1381 DCHECK(end.isNotNull()); |
| 1353 DCHECK(start.isConnected()) << start; | 1382 DCHECK(start.isConnected()) << start; |
| 1354 DCHECK(end.isConnected()) << end; | 1383 DCHECK(end.isConnected()) << end; |
| 1355 DCHECK(Position::commonAncestorTreeScope(start, end)) << start << " " << end; | 1384 DCHECK(Position::commonAncestorTreeScope(start, end)) << start << " " << end; |
| 1356 DCHECK_LE(start, end); | 1385 DCHECK_LE(start, end); |
| 1357 // FIXME: We should assert that start/end are not in the middle of a text node
. | 1386 // FIXME: We should assert that start/end are not in the middle of a text |
| 1387 // node. |
| 1358 | 1388 |
| 1359 Position pushDownStart = mostForwardCaretPosition(start); | 1389 Position pushDownStart = mostForwardCaretPosition(start); |
| 1360 // If the pushDownStart is at the end of a text node, then this node is not fu
lly selected. | 1390 // If the pushDownStart is at the end of a text node, then this node is not |
| 1361 // Move it to the next deep quivalent position to avoid removing the style fro
m this node. | 1391 // fully selected. Move it to the next deep quivalent position to avoid |
| 1362 // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</di
v></b>, we want Position("world", 0) instead. | 1392 // removing the style from this node. e.g. if pushDownStart was at |
| 1393 // Position("hello", 5) in <b>hello<div>world</div></b>, we want |
| 1394 // Position("world", 0) instead. |
| 1363 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); | 1395 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); |
| 1364 if (pushDownStartContainer && pushDownStartContainer->isTextNode() && | 1396 if (pushDownStartContainer && pushDownStartContainer->isTextNode() && |
| 1365 pushDownStart.computeOffsetInContainerNode() == | 1397 pushDownStart.computeOffsetInContainerNode() == |
| 1366 pushDownStartContainer->maxCharacterOffset()) | 1398 pushDownStartContainer->maxCharacterOffset()) |
| 1367 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); | 1399 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); |
| 1368 Position pushDownEnd = mostBackwardCaretPosition(end); | 1400 Position pushDownEnd = mostBackwardCaretPosition(end); |
| 1369 // If pushDownEnd is at the start of a text node, then this node is not fully
selected. | 1401 // If pushDownEnd is at the start of a text node, then this node is not fully |
| 1370 // Move it to the previous deep equivalent position to avoid removing the styl
e from this node. | 1402 // selected. Move it to the previous deep equivalent position to avoid |
| 1403 // removing the style from this node. |
| 1371 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); | 1404 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); |
| 1372 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && | 1405 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && |
| 1373 !pushDownEnd.computeOffsetInContainerNode()) | 1406 !pushDownEnd.computeOffsetInContainerNode()) |
| 1374 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); | 1407 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); |
| 1375 | 1408 |
| 1376 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode(), | 1409 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode(), |
| 1377 editingState); | 1410 editingState); |
| 1378 if (editingState->isAborted()) | 1411 if (editingState->isAborted()) |
| 1379 return; | 1412 return; |
| 1380 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode(), editingState); | 1413 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode(), editingState); |
| 1381 if (editingState->isAborted()) | 1414 if (editingState->isAborted()) |
| 1382 return; | 1415 return; |
| 1383 | 1416 |
| 1384 // The s and e variables store the positions used to set the ending selection
after style removal | 1417 // The s and e variables store the positions used to set the ending selection |
| 1385 // takes place. This will help callers to recognize when either the start node
or the end node | 1418 // after style removal takes place. This will help callers to recognize when |
| 1386 // are removed from the document during the work of this function. | 1419 // either the start node or the end node are removed from the document during |
| 1387 // If pushDownInlineStyleAroundNode has pruned start.anchorNode() or end.ancho
rNode(), | 1420 // the work of this function. |
| 1388 // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAroundNo
de won't prune. | 1421 // If pushDownInlineStyleAroundNode has pruned start.anchorNode() or |
| 1422 // end.anchorNode(), use pushDownStart or pushDownEnd instead, which |
| 1423 // pushDownInlineStyleAroundNode won't prune. |
| 1389 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; | 1424 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; |
| 1390 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; | 1425 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; |
| 1391 | 1426 |
| 1392 // Current ending selection resetting algorithm assumes |start| and |end| | 1427 // Current ending selection resetting algorithm assumes |start| and |end| |
| 1393 // are in a same DOM tree even if they are not in document. | 1428 // are in a same DOM tree even if they are not in document. |
| 1394 if (!Position::commonAncestorTreeScope(start, end)) | 1429 if (!Position::commonAncestorTreeScope(start, end)) |
| 1395 return; | 1430 return; |
| 1396 | 1431 |
| 1397 Node* node = start.anchorNode(); | 1432 Node* node = start.anchorNode(); |
| 1398 while (node) { | 1433 while (node) { |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1453 break; | 1488 break; |
| 1454 node = next; | 1489 node = next; |
| 1455 } | 1490 } |
| 1456 | 1491 |
| 1457 updateStartEnd(s, e); | 1492 updateStartEnd(s, e); |
| 1458 } | 1493 } |
| 1459 | 1494 |
| 1460 bool ApplyStyleCommand::elementFullySelected(HTMLElement& element, | 1495 bool ApplyStyleCommand::elementFullySelected(HTMLElement& element, |
| 1461 const Position& start, | 1496 const Position& start, |
| 1462 const Position& end) const { | 1497 const Position& end) const { |
| 1463 // The tree may have changed and Position::upstream() relies on an up-to-date
layout. | 1498 // The tree may have changed and Position::upstream() relies on an up-to-date |
| 1499 // layout. |
| 1464 element.document().updateStyleAndLayoutIgnorePendingStylesheets(); | 1500 element.document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 1465 | 1501 |
| 1466 return comparePositions(firstPositionInOrBeforeNode(&element), start) >= 0 && | 1502 return comparePositions(firstPositionInOrBeforeNode(&element), start) >= 0 && |
| 1467 comparePositions( | 1503 comparePositions( |
| 1468 mostBackwardCaretPosition(lastPositionInOrAfterNode(&element)), | 1504 mostBackwardCaretPosition(lastPositionInOrAfterNode(&element)), |
| 1469 end) <= 0; | 1505 end) <= 0; |
| 1470 } | 1506 } |
| 1471 | 1507 |
| 1472 void ApplyStyleCommand::splitTextAtStart(const Position& start, | 1508 void ApplyStyleCommand::splitTextAtStart(const Position& start, |
| 1473 const Position& end) { | 1509 const Position& end) { |
| (...skipping 228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1702 if (mergedElement->isElementNode() && hasEditableStyle(*mergedElement) && | 1738 if (mergedElement->isElementNode() && hasEditableStyle(*mergedElement) && |
| 1703 areIdenticalElements(toElement(*previousSibling), | 1739 areIdenticalElements(toElement(*previousSibling), |
| 1704 toElement(*mergedElement))) { | 1740 toElement(*mergedElement))) { |
| 1705 mergeIdenticalElements(toElement(previousSibling), | 1741 mergeIdenticalElements(toElement(previousSibling), |
| 1706 toElement(mergedElement), editingState); | 1742 toElement(mergedElement), editingState); |
| 1707 if (editingState->isAborted()) | 1743 if (editingState->isAborted()) |
| 1708 return; | 1744 return; |
| 1709 } | 1745 } |
| 1710 } | 1746 } |
| 1711 | 1747 |
| 1712 // FIXME: We should probably call updateStartEnd if the start or end was in th
e node | 1748 // FIXME: We should probably call updateStartEnd if the start or end was in |
| 1713 // range so that the endingSelection() is canonicalized. See the comments at
the end of | 1749 // the node range so that the endingSelection() is canonicalized. See the |
| 1714 // VisibleSelection::validate(). | 1750 // comments at the end of VisibleSelection::validate(). |
| 1715 } | 1751 } |
| 1716 | 1752 |
| 1717 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, | 1753 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, |
| 1718 HTMLElement* block) { | 1754 HTMLElement* block) { |
| 1719 // Do not check for legacy styles here. Those styles, like <B> and <I>, only a
pply for | 1755 // Do not check for legacy styles here. Those styles, like <B> and <I>, only |
| 1720 // inline content. | 1756 // apply for inline content. |
| 1721 if (!block) | 1757 if (!block) |
| 1722 return; | 1758 return; |
| 1723 | 1759 |
| 1724 String cssStyle = styleChange.cssStyle(); | 1760 String cssStyle = styleChange.cssStyle(); |
| 1725 StringBuilder cssText; | 1761 StringBuilder cssText; |
| 1726 cssText.append(cssStyle); | 1762 cssText.append(cssStyle); |
| 1727 if (const StylePropertySet* decl = block->inlineStyle()) { | 1763 if (const StylePropertySet* decl = block->inlineStyle()) { |
| 1728 if (!cssStyle.isEmpty()) | 1764 if (!cssStyle.isEmpty()) |
| 1729 cssText.append(' '); | 1765 cssText.append(' '); |
| 1730 cssText.append(decl->asText()); | 1766 cssText.append(decl->asText()); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1754 } | 1790 } |
| 1755 | 1791 |
| 1756 applyInlineStyleChange(start, passedEnd, styleChange, DoNotAddStyledElement, | 1792 applyInlineStyleChange(start, passedEnd, styleChange, DoNotAddStyledElement, |
| 1757 editingState); | 1793 editingState); |
| 1758 } | 1794 } |
| 1759 | 1795 |
| 1760 Position ApplyStyleCommand::positionToComputeInlineStyleChange( | 1796 Position ApplyStyleCommand::positionToComputeInlineStyleChange( |
| 1761 Node* startNode, | 1797 Node* startNode, |
| 1762 Member<HTMLSpanElement>& dummyElement, | 1798 Member<HTMLSpanElement>& dummyElement, |
| 1763 EditingState* editingState) { | 1799 EditingState* editingState) { |
| 1764 // It's okay to obtain the style at the startNode because we've removed all re
levant styles from the current run. | 1800 // It's okay to obtain the style at the startNode because we've removed all |
| 1801 // relevant styles from the current run. |
| 1765 if (!startNode->isElementNode()) { | 1802 if (!startNode->isElementNode()) { |
| 1766 dummyElement = HTMLSpanElement::create(document()); | 1803 dummyElement = HTMLSpanElement::create(document()); |
| 1767 insertNodeAt(dummyElement, Position::beforeNode(startNode), editingState); | 1804 insertNodeAt(dummyElement, Position::beforeNode(startNode), editingState); |
| 1768 if (editingState->isAborted()) | 1805 if (editingState->isAborted()) |
| 1769 return Position(); | 1806 return Position(); |
| 1770 return Position::beforeNode(dummyElement); | 1807 return Position::beforeNode(dummyElement); |
| 1771 } | 1808 } |
| 1772 | 1809 |
| 1773 return firstPositionInOrBeforeNode(startNode); | 1810 return firstPositionInOrBeforeNode(startNode); |
| 1774 } | 1811 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 1797 if (isHTMLSpanElement(*containerElement) || | 1834 if (isHTMLSpanElement(*containerElement) || |
| 1798 (styleContainerIsNotSpan && containerElement->hasChildren())) | 1835 (styleContainerIsNotSpan && containerElement->hasChildren())) |
| 1799 styleContainer = toHTMLElement(container); | 1836 styleContainer = toHTMLElement(container); |
| 1800 } | 1837 } |
| 1801 if (!container->hasChildren()) | 1838 if (!container->hasChildren()) |
| 1802 break; | 1839 break; |
| 1803 startNode = container->firstChild(); | 1840 startNode = container->firstChild(); |
| 1804 endNode = container->lastChild(); | 1841 endNode = container->lastChild(); |
| 1805 } | 1842 } |
| 1806 | 1843 |
| 1807 // Font tags need to go outside of CSS so that CSS font sizes override leagcy
font sizes. | 1844 // Font tags need to go outside of CSS so that CSS font sizes override leagcy |
| 1845 // font sizes. |
| 1808 if (styleChange.applyFontColor() || styleChange.applyFontFace() || | 1846 if (styleChange.applyFontColor() || styleChange.applyFontFace() || |
| 1809 styleChange.applyFontSize()) { | 1847 styleChange.applyFontSize()) { |
| 1810 if (fontContainer) { | 1848 if (fontContainer) { |
| 1811 if (styleChange.applyFontColor()) | 1849 if (styleChange.applyFontColor()) |
| 1812 setNodeAttribute(fontContainer, colorAttr, | 1850 setNodeAttribute(fontContainer, colorAttr, |
| 1813 AtomicString(styleChange.fontColor())); | 1851 AtomicString(styleChange.fontColor())); |
| 1814 if (styleChange.applyFontFace()) | 1852 if (styleChange.applyFontFace()) |
| 1815 setNodeAttribute(fontContainer, faceAttr, | 1853 setNodeAttribute(fontContainer, faceAttr, |
| 1816 AtomicString(styleChange.fontFace())); | 1854 AtomicString(styleChange.fontFace())); |
| 1817 if (styleChange.applyFontSize()) | 1855 if (styleChange.applyFontSize()) |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1974 | 2012 |
| 1975 DEFINE_TRACE(ApplyStyleCommand) { | 2013 DEFINE_TRACE(ApplyStyleCommand) { |
| 1976 visitor->trace(m_style); | 2014 visitor->trace(m_style); |
| 1977 visitor->trace(m_start); | 2015 visitor->trace(m_start); |
| 1978 visitor->trace(m_end); | 2016 visitor->trace(m_end); |
| 1979 visitor->trace(m_styledInlineElement); | 2017 visitor->trace(m_styledInlineElement); |
| 1980 CompositeEditCommand::trace(visitor); | 2018 CompositeEditCommand::trace(visitor); |
| 1981 } | 2019 } |
| 1982 | 2020 |
| 1983 } // namespace blink | 2021 } // namespace blink |
| OLD | NEW |