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

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

Issue 2685793002: Remove Apple-style-span CSS class support (Closed)
Patch Set: Update test cases Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. 2 * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved. 3 * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions 6 * modification, are permitted provided that the following conditions
7 * are met: 7 * are met:
8 * 1. Redistributions of source code must retain the above copyright 8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright 10 * 2. Redistributions in binary form must reproduce the above copyright
(...skipping 654 matching lines...) Expand 10 before | Expand all | Expand 10 after
665 if (editingState->isAborted()) 665 if (editingState->isAborted())
666 return; 666 return;
667 continue; 667 continue;
668 } 668 }
669 669
670 if (element->parentNode() && 670 if (element->parentNode() &&
671 hasRichlyEditableStyle(*element->parentNode()) && 671 hasRichlyEditableStyle(*element->parentNode()) &&
672 hasRichlyEditableStyle(*element)) { 672 hasRichlyEditableStyle(*element)) {
673 removeElementAttribute(element, contenteditableAttr); 673 removeElementAttribute(element, contenteditableAttr);
674 } 674 }
675
676 // WebKit used to not add display: inline and float: none on copy.
677 // Keep this code around for backward compatibility
678 if (isLegacyAppleHTMLSpanElement(element)) {
679 if (!element->hasChildren()) {
680 insertedNodes.willRemoveNodePreservingChildren(*element);
681 removeNodePreservingChildren(element, editingState);
682 if (editingState->isAborted())
683 return;
684 continue;
685 }
686 // There are other styles that style rules can give to style spans, but
687 // these are the two important ones because they'll prevent inserted
688 // content from appearing in the right paragraph.
689 // FIXME: Hyatt is concerned that selectively using display:inline will
690 // give inconsistent results. We already know one issue because td
691 // elements ignore their display property in quirks mode (which Mail.app
692 // is always in). We should look for an alternative.
693
694 // Mutate using the CSSOM wrapper so we get the same event behavior as a
695 // script.
696 if (isEnclosingBlock(element)) {
697 element->style()->setPropertyInternal(CSSPropertyDisplay, String(),
698 "inline", false,
699 IGNORE_EXCEPTION_FOR_TESTING);
700 }
701 if (element->layoutObject() &&
702 element->layoutObject()->style()->isFloating()) {
703 element->style()->setPropertyInternal(CSSPropertyFloat, String(),
704 "none", false,
705 IGNORE_EXCEPTION_FOR_TESTING);
706 }
707 }
708 } 675 }
709 } 676 }
710 677
711 static bool isProhibitedParagraphChild(const AtomicString& name) { 678 static bool isProhibitedParagraphChild(const AtomicString& name) {
712 // https://dvcs.w3.org/hg/editing/raw-file/57abe6d3cb60/editing.html#prohibite d-paragraph-child 679 // https://dvcs.w3.org/hg/editing/raw-file/57abe6d3cb60/editing.html#prohibite d-paragraph-child
713 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, elements, 680 DEFINE_STATIC_LOCAL(HashSet<AtomicString>, elements,
714 ({ 681 ({
715 addressTag.localName(), articleTag.localName(), 682 addressTag.localName(), articleTag.localName(),
716 asideTag.localName(), blockquoteTag.localName(), 683 asideTag.localName(), blockquoteTag.localName(),
717 captionTag.localName(), centerTag.localName(), 684 captionTag.localName(), centerTag.localName(),
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after
891 858
892 const HTMLElement& element = toHTMLElement(*node); 859 const HTMLElement& element = toHTMLElement(*node);
893 return isListItem(node) || isTableCell(node) || element.hasTagName(preTag) || 860 return isListItem(node) || isTableCell(node) || element.hasTagName(preTag) ||
894 element.hasTagName(h1Tag) || element.hasTagName(h2Tag) || 861 element.hasTagName(h1Tag) || element.hasTagName(h2Tag) ||
895 element.hasTagName(h3Tag) || element.hasTagName(h4Tag) || 862 element.hasTagName(h3Tag) || element.hasTagName(h4Tag) ||
896 element.hasTagName(h5Tag) || element.hasTagName(h6Tag); 863 element.hasTagName(h5Tag) || element.hasTagName(h6Tag);
897 } 864 }
898 865
899 // Remove style spans before insertion if they are unnecessary. It's faster 866 // Remove style spans before insertion if they are unnecessary. It's faster
900 // because we'll avoid doing a layout. 867 // because we'll avoid doing a layout.
901 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, 868 static void handleStyleSpansBeforeInsertion(ReplacementFragment& fragment,
902 const Position& insertionPos) { 869 const Position& insertionPos) {
903 Node* topNode = fragment.firstChild(); 870 Node* topNode = fragment.firstChild();
904 if (!isHTMLSpanElement(topNode)) 871 if (!isHTMLSpanElement(topNode))
905 return false; 872 return;
906 873
907 // Handling the case where we are doing Paste as Quotation or pasting into 874 // Handling the case where we are doing Paste as Quotation or pasting into
908 // quoted content is more complicated (see handleStyleSpans) and doesn't 875 // quoted content is more complicated (see handleStyleSpans) and doesn't
909 // receive the optimization. 876 // receive the optimization.
910 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || 877 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) ||
911 enclosingNodeOfType(firstPositionInOrBeforeNode(topNode), 878 enclosingNodeOfType(firstPositionInOrBeforeNode(topNode),
912 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)) 879 isMailHTMLBlockquoteElement, CanCrossEditingBoundary))
913 return false; 880 return;
914 881
915 // Remove style spans to follow the styles of parent block element when 882 // Remove style spans to follow the styles of parent block element when
916 // |fragment| becomes a part of it. See bugs http://crbug.com/226941 and 883 // |fragment| becomes a part of it. See bugs http://crbug.com/226941 and
917 // http://crbug.com/335955. 884 // http://crbug.com/335955.
918 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode); 885 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode);
919 const Node* node = insertionPos.anchorNode(); 886 const Node* node = insertionPos.anchorNode();
920 // |node| can be an inline element like <br> under <li> 887 // |node| can be an inline element like <br> under <li>
921 // e.g.) editing/execCommand/switch-list-type.html 888 // e.g.) editing/execCommand/switch-list-type.html
922 // editing/deleting/backspace-merge-into-block.html 889 // editing/deleting/backspace-merge-into-block.html
923 if (isInline(node)) { 890 if (isInline(node)) {
924 node = enclosingBlock(insertionPos.anchorNode()); 891 node = enclosingBlock(insertionPos.anchorNode());
925 if (!node) 892 if (!node)
926 return false; 893 return;
927 } 894 }
928 895
929 if (followBlockElementStyle(node)) { 896 if (followBlockElementStyle(node)) {
930 fragment.removeNodePreservingChildren(wrappingStyleSpan); 897 fragment.removeNodePreservingChildren(wrappingStyleSpan);
931 return true; 898 return;
932 } 899 }
933 900
934 // Either there are no style spans in the fragment or a WebKit client has
935 // added content to the fragment before inserting it. Look for and handle
936 // style spans after insertion.
937 if (!isLegacyAppleHTMLSpanElement(topNode))
938 return false;
939
940 EditingStyle* styleAtInsertionPos = 901 EditingStyle* styleAtInsertionPos =
941 EditingStyle::create(insertionPos.parentAnchoredEquivalent()); 902 EditingStyle::create(insertionPos.parentAnchoredEquivalent());
942 String styleText = styleAtInsertionPos->style()->asText(); 903 String styleText = styleAtInsertionPos->style()->asText();
943 904
944 // FIXME: This string comparison is a naive way of comparing two styles. 905 // FIXME: This string comparison is a naive way of comparing two styles.
945 // We should be taking the diff and check that the diff is empty. 906 // We should be taking the diff and check that the diff is empty.
946 if (styleText != wrappingStyleSpan->getAttribute(styleAttr)) 907 if (styleText != wrappingStyleSpan->getAttribute(styleAttr))
947 return false; 908 return;
948 909
949 fragment.removeNodePreservingChildren(wrappingStyleSpan); 910 fragment.removeNodePreservingChildren(wrappingStyleSpan);
950 return true;
951 }
952
953 // At copy time, WebKit wraps copied content in a span that contains the source
954 // document's default styles. If the copied Range inherits any other styles
955 // from its ancestors, we put those styles on a second span. This function
956 // removes redundant styles from those spans, and removes the
957 // spans if all their styles are redundant.
958 // We should remove the Apple-style-span class when we're done, see
959 // <rdar://problem/5685600>.
960 // We should remove styles from spans that are overridden by all of their
961 // children, either here or at copy time.
962 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes,
963 EditingState* editingState) {
964 if (!insertedNodes.firstNodeInserted())
965 return;
966
967 HTMLSpanElement* wrappingStyleSpan = nullptr;
968 // The style span that contains the source document's default style should be
969 // at the top of the fragment, but Mail sometimes adds a wrapper (for Paste As
970 // Quotation), so search for the top level style span instead of assuming it's
971 // at the top.
972
973 for (Node& node :
974 NodeTraversal::startsAt(*insertedNodes.firstNodeInserted())) {
975 if (isLegacyAppleHTMLSpanElement(&node)) {
976 wrappingStyleSpan = toHTMLSpanElement(&node);
977 break;
978 }
979 }
980
981 // There might not be any style spans if we're pasting from another
982 // application or if we are here because of a
983 // document.execCommand("InsertHTML", ...) call.
984 if (!wrappingStyleSpan)
985 return;
986
987 EditingStyle* style = EditingStyle::create(wrappingStyleSpan->inlineStyle());
988 ContainerNode* context = wrappingStyleSpan->parentNode();
989
990 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if
991 // you're pasting into a quoted region, styles from blockquoteElement are
992 // allowed to override those from the source document, see
993 // <rdar://problem/4930986> and <rdar://problem/5089327>.
994 HTMLQuoteElement* blockquoteElement =
995 isMailPasteAsQuotationHTMLBlockQuoteElement(context)
996 ? toHTMLQuoteElement(context)
997 : toHTMLQuoteElement(enclosingNodeOfType(
998 Position::firstPositionInNode(context),
999 isMailHTMLBlockquoteElement, CanCrossEditingBoundary));
1000 if (blockquoteElement)
1001 context = document().documentElement();
1002
1003 // This operation requires that only editing styles to be removed from
1004 // sourceDocumentStyle.
1005 style->prepareToApplyAt(Position::firstPositionInNode(context));
1006
1007 // Remove block properties in the span's style. This prevents properties that
1008 // probably have no effect currently from affecting blocks later if the style
1009 // is cloned for a new block element during a future
1010 // editing operation.
1011 // FIXME: They *can* have an effect currently if blocks beneath the style span
1012 // aren't individually marked with block styles by the editing engine used to
1013 // style them. WebKit doesn't do this, but others might.
1014 style->removeBlockProperties();
1015
1016 if (style->isEmpty() || !wrappingStyleSpan->hasChildren()) {
1017 insertedNodes.willRemoveNodePreservingChildren(*wrappingStyleSpan);
1018 removeNodePreservingChildren(wrappingStyleSpan, editingState);
1019 } else {
1020 setNodeAttribute(wrappingStyleSpan, styleAttr,
1021 AtomicString(style->style()->asText()));
1022 }
1023 } 911 }
1024 912
1025 void ReplaceSelectionCommand::mergeEndIfNeeded(EditingState* editingState) { 913 void ReplaceSelectionCommand::mergeEndIfNeeded(EditingState* editingState) {
1026 if (!m_shouldMergeEnd) 914 if (!m_shouldMergeEnd)
1027 return; 915 return;
1028 916
1029 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); 917 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
1030 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); 918 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
1031 919
1032 // Bail to avoid infinite recursion. 920 // Bail to avoid infinite recursion.
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after
1366 1254
1367 // We don't want the destination to end up inside nodes that weren't selected. 1255 // We don't want the destination to end up inside nodes that weren't selected.
1368 // To avoid that, we move the position forward without changing the visible 1256 // To avoid that, we move the position forward without changing the visible
1369 // position so we're still at the same visible location, but outside of 1257 // position so we're still at the same visible location, but outside of
1370 // preceding tags. 1258 // preceding tags.
1371 insertionPos = positionAvoidingPrecedingNodes(insertionPos); 1259 insertionPos = positionAvoidingPrecedingNodes(insertionPos);
1372 1260
1373 // Paste into run of tabs splits the tab span. 1261 // Paste into run of tabs splits the tab span.
1374 insertionPos = positionOutsideTabSpan(insertionPos); 1262 insertionPos = positionOutsideTabSpan(insertionPos);
1375 1263
1376 bool handledStyleSpans = 1264 handleStyleSpansBeforeInsertion(fragment, insertionPos);
1377 handleStyleSpansBeforeInsertion(fragment, insertionPos);
1378 1265
1379 // We're finished if there is nothing to add. 1266 // We're finished if there is nothing to add.
1380 if (fragment.isEmpty() || !fragment.firstChild()) 1267 if (fragment.isEmpty() || !fragment.firstChild())
1381 return; 1268 return;
1382 1269
1383 // If we are not trying to match the destination style we prefer a position 1270 // If we are not trying to match the destination style we prefer a position
1384 // that is outside inline elements that provide style. 1271 // that is outside inline elements that provide style.
1385 // This way we can produce a less verbose markup. 1272 // This way we can produce a less verbose markup.
1386 // We can skip this optimization for fragments not wrapped in one of 1273 // We can skip this optimization for fragments not wrapped in one of
1387 // our style spans and for positions inside list items 1274 // our style spans and for positions inside list items
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
1429 1316
1430 InsertedNodes insertedNodes; 1317 InsertedNodes insertedNodes;
1431 insertedNodes.setRefNode(fragment.firstChild()); 1318 insertedNodes.setRefNode(fragment.firstChild());
1432 DCHECK(insertedNodes.refNode()); 1319 DCHECK(insertedNodes.refNode());
1433 Node* node = insertedNodes.refNode()->nextSibling(); 1320 Node* node = insertedNodes.refNode()->nextSibling();
1434 1321
1435 fragment.removeNode(insertedNodes.refNode()); 1322 fragment.removeNode(insertedNodes.refNode());
1436 1323
1437 Element* blockStart = enclosingBlock(insertionPos.anchorNode()); 1324 Element* blockStart = enclosingBlock(insertionPos.anchorNode());
1438 if ((isHTMLListElement(insertedNodes.refNode()) || 1325 if ((isHTMLListElement(insertedNodes.refNode()) ||
1439 (isLegacyAppleHTMLSpanElement(insertedNodes.refNode()) && 1326 (isHTMLListElement(insertedNodes.refNode()->firstChild()))) &&
1440 isHTMLListElement(insertedNodes.refNode()->firstChild()))) &&
1441 blockStart && blockStart->layoutObject()->isListItem() && 1327 blockStart && blockStart->layoutObject()->isListItem() &&
1442 hasEditableStyle(*blockStart->parentNode())) { 1328 hasEditableStyle(*blockStart->parentNode())) {
1443 insertedNodes.setRefNode( 1329 insertedNodes.setRefNode(
1444 insertAsListItems(toHTMLElement(insertedNodes.refNode()), blockStart, 1330 insertAsListItems(toHTMLElement(insertedNodes.refNode()), blockStart,
1445 insertionPos, insertedNodes, editingState)); 1331 insertionPos, insertedNodes, editingState));
1446 if (editingState->isAborted()) 1332 if (editingState->isAborted())
1447 return; 1333 return;
1448 } else { 1334 } else {
1449 insertNodeAt(insertedNodes.refNode(), insertionPos, editingState); 1335 insertNodeAt(insertedNodes.refNode(), insertionPos, editingState);
1450 if (editingState->isAborted()) 1336 if (editingState->isAborted())
(...skipping 21 matching lines...) Expand all
1472 1358
1473 insertedNodes.setRefNode(node); 1359 insertedNodes.setRefNode(node);
1474 if (node && plainTextFragment) 1360 if (node && plainTextFragment)
1475 plainTextFragment = isPlainTextMarkup(node); 1361 plainTextFragment = isPlainTextMarkup(node);
1476 node = next; 1362 node = next;
1477 } 1363 }
1478 1364
1479 if (isRichlyEditablePosition(insertionPos)) 1365 if (isRichlyEditablePosition(insertionPos))
1480 removeUnrenderedTextNodesAtEnds(insertedNodes); 1366 removeUnrenderedTextNodesAtEnds(insertedNodes);
1481 1367
1482 if (!handledStyleSpans) {
1483 handleStyleSpans(insertedNodes, editingState);
1484 if (editingState->isAborted())
1485 return;
1486 }
1487
1488 document().updateStyleAndLayoutIgnorePendingStylesheets(); 1368 document().updateStyleAndLayoutIgnorePendingStylesheets();
1489 1369
1490 // Mutation events (bug 20161) may have already removed the inserted content 1370 // Mutation events (bug 20161) may have already removed the inserted content
1491 if (!insertedNodes.firstNodeInserted() || 1371 if (!insertedNodes.firstNodeInserted() ||
1492 !insertedNodes.firstNodeInserted()->isConnected()) 1372 !insertedNodes.firstNodeInserted()->isConnected())
1493 return; 1373 return;
1494 1374
1495 // Scripts specified in javascript protocol may remove 1375 // Scripts specified in javascript protocol may remove
1496 // |enclosingBlockOfInsertionPos| during insertion, e.g. <iframe 1376 // |enclosingBlockOfInsertionPos| during insertion, e.g. <iframe
1497 // src="javascript:..."> 1377 // src="javascript:...">
(...skipping 642 matching lines...) Expand 10 before | Expand all | Expand 10 after
2140 visitor->trace(m_startOfInsertedContent); 2020 visitor->trace(m_startOfInsertedContent);
2141 visitor->trace(m_endOfInsertedContent); 2021 visitor->trace(m_endOfInsertedContent);
2142 visitor->trace(m_insertionStyle); 2022 visitor->trace(m_insertionStyle);
2143 visitor->trace(m_documentFragment); 2023 visitor->trace(m_documentFragment);
2144 visitor->trace(m_startOfInsertedRange); 2024 visitor->trace(m_startOfInsertedRange);
2145 visitor->trace(m_endOfInsertedRange); 2025 visitor->trace(m_endOfInsertedRange);
2146 CompositeEditCommand::trace(visitor); 2026 CompositeEditCommand::trace(visitor);
2147 } 2027 }
2148 2028
2149 } // namespace blink 2029 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698