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

Side by Side Diff: Source/core/editing/ReplaceSelectionCommand.cpp

Issue 423413005: Use tighter typing in editing: ReplaceSelectionCommand (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Rebase Created 6 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « Source/core/editing/ReplaceSelectionCommand.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
46 #include "core/editing/VisibleUnits.h" 46 #include "core/editing/VisibleUnits.h"
47 #include "core/editing/htmlediting.h" 47 #include "core/editing/htmlediting.h"
48 #include "core/editing/markup.h" 48 #include "core/editing/markup.h"
49 #include "core/events/BeforeTextInsertedEvent.h" 49 #include "core/events/BeforeTextInsertedEvent.h"
50 #include "core/frame/LocalFrame.h" 50 #include "core/frame/LocalFrame.h"
51 #include "core/frame/UseCounter.h" 51 #include "core/frame/UseCounter.h"
52 #include "core/html/HTMLBRElement.h" 52 #include "core/html/HTMLBRElement.h"
53 #include "core/html/HTMLElement.h" 53 #include "core/html/HTMLElement.h"
54 #include "core/html/HTMLInputElement.h" 54 #include "core/html/HTMLInputElement.h"
55 #include "core/html/HTMLLIElement.h" 55 #include "core/html/HTMLLIElement.h"
56 #include "core/html/HTMLQuoteElement.h"
57 #include "core/html/HTMLSelectElement.h"
56 #include "core/html/HTMLSpanElement.h" 58 #include "core/html/HTMLSpanElement.h"
57 #include "core/rendering/RenderObject.h" 59 #include "core/rendering/RenderObject.h"
58 #include "core/rendering/RenderText.h" 60 #include "core/rendering/RenderText.h"
59 #include "wtf/StdLibExtras.h" 61 #include "wtf/StdLibExtras.h"
60 #include "wtf/Vector.h" 62 #include "wtf/Vector.h"
61 63
62 namespace blink { 64 namespace blink {
63 65
64 using namespace HTMLNames; 66 using namespace HTMLNames;
65 67
66 enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment }; 68 enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
67 69
68 // --- ReplacementFragment helper class 70 // --- ReplacementFragment helper class
69 71
70 class ReplacementFragment FINAL { 72 class ReplacementFragment FINAL {
71 WTF_MAKE_NONCOPYABLE(ReplacementFragment); 73 WTF_MAKE_NONCOPYABLE(ReplacementFragment);
72 STACK_ALLOCATED(); 74 STACK_ALLOCATED();
73 public: 75 public:
74 ReplacementFragment(Document*, DocumentFragment*, const VisibleSelection&); 76 ReplacementFragment(Document*, DocumentFragment*, const VisibleSelection&);
75 77
76 Node* firstChild() const; 78 Node* firstChild() const;
77 Node* lastChild() const; 79 Node* lastChild() const;
78 80
79 bool isEmpty() const; 81 bool isEmpty() const;
80 82
81 bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAt Start; } 83 bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAt Start; }
82 bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEn d; } 84 bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEn d; }
83 85
84 void removeNode(PassRefPtrWillBeRawPtr<Node>); 86 void removeNode(PassRefPtrWillBeRawPtr<Node>);
85 void removeNodePreservingChildren(PassRefPtrWillBeRawPtr<Node>); 87 void removeNodePreservingChildren(PassRefPtrWillBeRawPtr<ContainerNode>);
86 88
87 private: 89 private:
88 PassRefPtrWillBeRawPtr<Element> insertFragmentForTestRendering(Node* rootEdi tableNode); 90 PassRefPtrWillBeRawPtr<HTMLElement> insertFragmentForTestRendering(Element* rootEditableElement);
89 void removeUnrenderedNodes(Node*); 91 void removeUnrenderedNodes(ContainerNode*);
90 void restoreAndRemoveTestRenderingNodesToFragment(Element*); 92 void restoreAndRemoveTestRenderingNodesToFragment(ContainerNode*);
leviw_travelin_and_unemployed 2014/08/06 20:58:07 This is less tight typing. Why?
Inactive 2014/08/06 21:00:05 Hmm, This is a very good question. It looks like a
Inactive 2014/08/06 21:07:49 I probably changed it to HTMLElement*, then notice
91 void removeInterchangeNodes(Node*); 93 void removeInterchangeNodes(ContainerNode*);
92 94
93 void insertNodeBefore(PassRefPtrWillBeRawPtr<Node>, Node* refNode); 95 void insertNodeBefore(PassRefPtrWillBeRawPtr<Node>, Node* refNode);
94 96
95 RefPtrWillBeMember<Document> m_document; 97 RefPtrWillBeMember<Document> m_document;
96 RefPtrWillBeMember<DocumentFragment> m_fragment; 98 RefPtrWillBeMember<DocumentFragment> m_fragment;
97 bool m_hasInterchangeNewlineAtStart; 99 bool m_hasInterchangeNewlineAtStart;
98 bool m_hasInterchangeNewlineAtEnd; 100 bool m_hasInterchangeNewlineAtEnd;
99 }; 101 };
100 102
101 static bool isInterchangeNewlineNode(const Node *node) 103 static bool isInterchangeHTMLBRElement(const Node* node)
102 { 104 {
103 DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchange Newline)); 105 DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchange Newline));
104 if (!isHTMLBRElement(node) || toElement(node)->getAttribute(classAttr) != in terchangeNewlineClassString) 106 if (!isHTMLBRElement(node) || toHTMLBRElement(node)->getAttribute(classAttr) != interchangeNewlineClassString)
105 return false; 107 return false;
106 UseCounter::count(node->document(), UseCounter::EditingAppleInterchangeNewli ne); 108 UseCounter::count(node->document(), UseCounter::EditingAppleInterchangeNewli ne);
107 return true; 109 return true;
108 } 110 }
109 111
110 static bool isInterchangeConvertedSpaceSpan(const Node *node) 112 static bool isHTMLInterchangeConvertedSpaceSpan(const Node* node)
111 { 113 {
112 DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSp ace)); 114 DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSp ace));
113 if (!node->isHTMLElement() || toHTMLElement(node)->getAttribute(classAttr) ! = convertedSpaceSpanClassString) 115 if (!node->isHTMLElement() || toHTMLElement(node)->getAttribute(classAttr) ! = convertedSpaceSpanClassString)
114 return false; 116 return false;
115 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpace); 117 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpace);
116 return true; 118 return true;
117 } 119 }
118 120
119 static Position positionAvoidingPrecedingNodes(Position pos) 121 static Position positionAvoidingPrecedingNodes(Position pos)
120 { 122 {
121 // If we're already on a break, it's probably a placeholder and we shouldn't change our position. 123 // If we're already on a break, it's probably a placeholder and we shouldn't change our position.
122 if (editingIgnoresContent(pos.deprecatedNode())) 124 if (editingIgnoresContent(pos.deprecatedNode()))
123 return pos; 125 return pos;
124 126
125 // We also stop when changing block flow elements because even though the vi sual position is the 127 // We also stop when changing block flow elements because even though the vi sual position is the
126 // same. E.g., 128 // same. E.g.,
127 // <div>foo^</div>^ 129 // <div>foo^</div>^
128 // The two positions above are the same visual position, but we want to stay in the same block. 130 // The two positions above are the same visual position, but we want to stay in the same block.
129 Node* enclosingBlockNode = enclosingBlock(pos.containerNode()); 131 Element* enclosingBlockElement = enclosingBlock(pos.containerNode());
130 for (Position nextPosition = pos; nextPosition.containerNode() != enclosingB lockNode; pos = nextPosition) { 132 for (Position nextPosition = pos; nextPosition.containerNode() != enclosingB lockElement; pos = nextPosition) {
131 if (lineBreakExistsAtPosition(pos)) 133 if (lineBreakExistsAtPosition(pos))
132 break; 134 break;
133 135
134 if (pos.containerNode()->nonShadowBoundaryParentNode()) 136 if (pos.containerNode()->nonShadowBoundaryParentNode())
135 nextPosition = positionInParentAfterNode(*pos.containerNode()); 137 nextPosition = positionInParentAfterNode(*pos.containerNode());
136 138
137 if (nextPosition == pos 139 if (nextPosition == pos
138 || enclosingBlock(nextPosition.containerNode()) != enclosingBlockNod e 140 || enclosingBlock(nextPosition.containerNode()) != enclosingBlockEle ment
139 || VisiblePosition(pos) != VisiblePosition(nextPosition)) 141 || VisiblePosition(pos) != VisiblePosition(nextPosition))
140 break; 142 break;
141 } 143 }
142 return pos; 144 return pos;
143 } 145 }
144 146
145 ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f ragment, const VisibleSelection& selection) 147 ReplacementFragment::ReplacementFragment(Document* document, DocumentFragment* f ragment, const VisibleSelection& selection)
146 : m_document(document), 148 : m_document(document),
147 m_fragment(fragment), 149 m_fragment(fragment),
148 m_hasInterchangeNewlineAtStart(false), 150 m_hasInterchangeNewlineAtStart(false),
149 m_hasInterchangeNewlineAtEnd(false) 151 m_hasInterchangeNewlineAtEnd(false)
150 { 152 {
151 if (!m_document) 153 if (!m_document)
152 return; 154 return;
153 if (!m_fragment || !m_fragment->hasChildren()) 155 if (!m_fragment || !m_fragment->hasChildren())
154 return; 156 return;
155 157
156 RefPtrWillBeRawPtr<Element> editableRoot = selection.rootEditableElement(); 158 RefPtrWillBeRawPtr<Element> editableRoot = selection.rootEditableElement();
157 ASSERT(editableRoot); 159 ASSERT(editableRoot);
158 if (!editableRoot) 160 if (!editableRoot)
159 return; 161 return;
160 162
161 Node* shadowAncestorNode; 163 Element* shadowAncestorElement;
162 if (editableRoot->isInShadowTree()) 164 if (editableRoot->isInShadowTree())
163 shadowAncestorNode = editableRoot->shadowHost(); 165 shadowAncestorElement = editableRoot->shadowHost();
164 else 166 else
165 shadowAncestorNode = editableRoot.get(); 167 shadowAncestorElement = editableRoot.get();
166 168
167 if (!editableRoot->getAttributeEventListener(EventTypeNames::webkitBeforeTex tInserted) && 169 if (!editableRoot->getAttributeEventListener(EventTypeNames::webkitBeforeTex tInserted)
168 // FIXME: Remove these checks once textareas and textfields actually reg ister an event handler. 170 // FIXME: Remove these checks once textareas and textfields actually reg ister an event handler.
169 !(shadowAncestorNode && shadowAncestorNode->renderer() && shadowAncestor Node->renderer()->isTextControl()) && 171 && !(shadowAncestorElement && shadowAncestorElement->renderer() && shado wAncestorElement->renderer()->isTextControl())
170 editableRoot->rendererIsRichlyEditable()) { 172 && editableRoot->rendererIsRichlyEditable()) {
171 removeInterchangeNodes(m_fragment.get()); 173 removeInterchangeNodes(m_fragment.get());
172 return; 174 return;
173 } 175 }
174 176
175 RefPtrWillBeRawPtr<Element> holder = insertFragmentForTestRendering(editable Root.get()); 177 RefPtrWillBeRawPtr<HTMLElement> holder = insertFragmentForTestRendering(edit ableRoot.get());
176 if (!holder) { 178 if (!holder) {
177 removeInterchangeNodes(m_fragment.get()); 179 removeInterchangeNodes(m_fragment.get());
178 return; 180 return;
179 } 181 }
180 182
181 RefPtrWillBeRawPtr<Range> range = VisibleSelection::selectionFromContentsOfN ode(holder.get()).toNormalizedRange(); 183 RefPtrWillBeRawPtr<Range> range = VisibleSelection::selectionFromContentsOfN ode(holder.get()).toNormalizedRange();
182 String text = plainText(range.get(), static_cast<TextIteratorBehavior>(TextI teratorEmitsOriginalText | TextIteratorIgnoresStyleVisibility)); 184 String text = plainText(range.get(), static_cast<TextIteratorBehavior>(TextI teratorEmitsOriginalText | TextIteratorIgnoresStyleVisibility));
183 185
184 removeInterchangeNodes(holder.get()); 186 removeInterchangeNodes(holder.get());
185 removeUnrenderedNodes(holder.get()); 187 removeUnrenderedNodes(holder.get());
(...skipping 14 matching lines...) Expand all
200 removeUnrenderedNodes(holder.get()); 202 removeUnrenderedNodes(holder.get());
201 restoreAndRemoveTestRenderingNodesToFragment(holder.get()); 203 restoreAndRemoveTestRenderingNodesToFragment(holder.get());
202 } 204 }
203 } 205 }
204 206
205 bool ReplacementFragment::isEmpty() const 207 bool ReplacementFragment::isEmpty() const
206 { 208 {
207 return (!m_fragment || !m_fragment->hasChildren()) && !m_hasInterchangeNewli neAtStart && !m_hasInterchangeNewlineAtEnd; 209 return (!m_fragment || !m_fragment->hasChildren()) && !m_hasInterchangeNewli neAtStart && !m_hasInterchangeNewlineAtEnd;
208 } 210 }
209 211
210 Node *ReplacementFragment::firstChild() const 212 Node* ReplacementFragment::firstChild() const
211 { 213 {
212 return m_fragment ? m_fragment->firstChild() : 0; 214 return m_fragment ? m_fragment->firstChild() : 0;
213 } 215 }
214 216
215 Node *ReplacementFragment::lastChild() const 217 Node* ReplacementFragment::lastChild() const
216 { 218 {
217 return m_fragment ? m_fragment->lastChild() : 0; 219 return m_fragment ? m_fragment->lastChild() : 0;
218 } 220 }
219 221
220 void ReplacementFragment::removeNodePreservingChildren(PassRefPtrWillBeRawPtr<No de> node) 222 void ReplacementFragment::removeNodePreservingChildren(PassRefPtrWillBeRawPtr<Co ntainerNode> node)
221 { 223 {
222 if (!node) 224 if (!node)
223 return; 225 return;
224 226
225 while (RefPtrWillBeRawPtr<Node> n = node->firstChild()) { 227 while (RefPtrWillBeRawPtr<Node> n = node->firstChild()) {
226 removeNode(n); 228 removeNode(n);
227 insertNodeBefore(n.release(), node.get()); 229 insertNodeBefore(n.release(), node.get());
228 } 230 }
229 removeNode(node); 231 removeNode(node);
230 } 232 }
(...skipping 15 matching lines...) Expand all
246 if (!node || !refNode) 248 if (!node || !refNode)
247 return; 249 return;
248 250
249 ContainerNode* parent = refNode->nonShadowBoundaryParentNode(); 251 ContainerNode* parent = refNode->nonShadowBoundaryParentNode();
250 if (!parent) 252 if (!parent)
251 return; 253 return;
252 254
253 parent->insertBefore(node, refNode); 255 parent->insertBefore(node, refNode);
254 } 256 }
255 257
256 PassRefPtrWillBeRawPtr<Element> ReplacementFragment::insertFragmentForTestRender ing(Node* rootEditableElement) 258 PassRefPtrWillBeRawPtr<HTMLElement> ReplacementFragment::insertFragmentForTestRe ndering(Element* rootEditableElement)
257 { 259 {
258 ASSERT(m_document); 260 ASSERT(m_document);
259 RefPtrWillBeRawPtr<Element> holder = createDefaultParagraphElement(*m_docume nt.get()); 261 RefPtrWillBeRawPtr<HTMLElement> holder = createDefaultParagraphElement(*m_do cument.get());
260 262
261 holder->appendChild(m_fragment); 263 holder->appendChild(m_fragment);
262 rootEditableElement->appendChild(holder.get()); 264 rootEditableElement->appendChild(holder.get());
263 m_document->updateLayoutIgnorePendingStylesheets(); 265 m_document->updateLayoutIgnorePendingStylesheets();
264 266
265 return holder.release(); 267 return holder.release();
266 } 268 }
267 269
268 void ReplacementFragment::restoreAndRemoveTestRenderingNodesToFragment(Element* holder) 270 void ReplacementFragment::restoreAndRemoveTestRenderingNodesToFragment(Container Node* holder)
269 { 271 {
270 if (!holder) 272 if (!holder)
271 return; 273 return;
272 274
273 while (RefPtrWillBeRawPtr<Node> node = holder->firstChild()) { 275 while (RefPtrWillBeRawPtr<Node> node = holder->firstChild()) {
274 holder->removeChild(node.get()); 276 holder->removeChild(node.get());
275 m_fragment->appendChild(node.get()); 277 m_fragment->appendChild(node.get());
276 } 278 }
277 279
278 removeNode(holder); 280 removeNode(holder);
279 } 281 }
280 282
281 void ReplacementFragment::removeUnrenderedNodes(Node* holder) 283 void ReplacementFragment::removeUnrenderedNodes(ContainerNode* holder)
282 { 284 {
283 WillBeHeapVector<RefPtrWillBeMember<Node> > unrendered; 285 WillBeHeapVector<RefPtrWillBeMember<Node> > unrendered;
284 286
285 for (Node* node = holder->firstChild(); node; node = NodeTraversal::next(*no de, holder)) 287 for (Node* node = holder->firstChild(); node; node = NodeTraversal::next(*no de, holder))
286 if (!isNodeRendered(node) && !isTableStructureNode(node)) 288 if (!isNodeRendered(node) && !isTableStructureNode(node))
287 unrendered.append(node); 289 unrendered.append(node);
288 290
289 size_t n = unrendered.size(); 291 size_t n = unrendered.size();
290 for (size_t i = 0; i < n; ++i) 292 for (size_t i = 0; i < n; ++i)
291 removeNode(unrendered[i]); 293 removeNode(unrendered[i]);
292 } 294 }
293 295
294 void ReplacementFragment::removeInterchangeNodes(Node* container) 296 void ReplacementFragment::removeInterchangeNodes(ContainerNode* container)
295 { 297 {
296 m_hasInterchangeNewlineAtStart = false; 298 m_hasInterchangeNewlineAtStart = false;
297 m_hasInterchangeNewlineAtEnd = false; 299 m_hasInterchangeNewlineAtEnd = false;
298 300
299 // Interchange newlines at the "start" of the incoming fragment must be 301 // Interchange newlines at the "start" of the incoming fragment must be
300 // either the first node in the fragment or the first leaf in the fragment. 302 // either the first node in the fragment or the first leaf in the fragment.
301 Node* node = container->firstChild(); 303 Node* node = container->firstChild();
302 while (node) { 304 while (node) {
303 if (isInterchangeNewlineNode(node)) { 305 if (isInterchangeHTMLBRElement(node)) {
304 m_hasInterchangeNewlineAtStart = true; 306 m_hasInterchangeNewlineAtStart = true;
305 removeNode(node); 307 removeNode(node);
306 break; 308 break;
307 } 309 }
308 node = node->firstChild(); 310 node = node->firstChild();
309 } 311 }
310 if (!container->hasChildren()) 312 if (!container->hasChildren())
311 return; 313 return;
312 // Interchange newlines at the "end" of the incoming fragment must be 314 // Interchange newlines at the "end" of the incoming fragment must be
313 // either the last node in the fragment or the last leaf in the fragment. 315 // either the last node in the fragment or the last leaf in the fragment.
314 node = container->lastChild(); 316 node = container->lastChild();
315 while (node) { 317 while (node) {
316 if (isInterchangeNewlineNode(node)) { 318 if (isInterchangeHTMLBRElement(node)) {
317 m_hasInterchangeNewlineAtEnd = true; 319 m_hasInterchangeNewlineAtEnd = true;
318 removeNode(node); 320 removeNode(node);
319 break; 321 break;
320 } 322 }
321 node = node->lastChild(); 323 node = node->lastChild();
322 } 324 }
323 325
324 node = container->firstChild(); 326 node = container->firstChild();
325 while (node) { 327 while (node) {
326 RefPtrWillBeRawPtr<Node> next = NodeTraversal::next(*node); 328 RefPtrWillBeRawPtr<Node> next = NodeTraversal::next(*node);
327 if (isInterchangeConvertedSpaceSpan(node)) { 329 if (isHTMLInterchangeConvertedSpaceSpan(node)) {
328 next = NodeTraversal::nextSkippingChildren(*node); 330 HTMLElement& element = toHTMLElement(*node);
329 removeNodePreservingChildren(node); 331 next = NodeTraversal::nextSkippingChildren(element);
332 removeNodePreservingChildren(&element);
330 } 333 }
331 node = next.get(); 334 node = next.get();
332 } 335 }
333 } 336 }
334 337
335 inline void ReplaceSelectionCommand::InsertedNodes::respondToNodeInsertion(Node& node) 338 inline void ReplaceSelectionCommand::InsertedNodes::respondToNodeInsertion(Node& node)
336 { 339 {
337 if (!m_firstNodeInserted) 340 if (!m_firstNodeInserted)
338 m_firstNodeInserted = &node; 341 m_firstNodeInserted = &node;
339 342
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
421 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary) ; 424 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary) ;
422 if (next.isNull()) 425 if (next.isNull())
423 return false; 426 return false;
424 427
425 return !selectionEndWasEndOfParagraph 428 return !selectionEndWasEndOfParagraph
426 && isEndOfParagraph(endOfInsertedContent) 429 && isEndOfParagraph(endOfInsertedContent)
427 && !isHTMLBRElement(*endOfInsertedContent.deepEquivalent().deprecatedNod e()) 430 && !isHTMLBRElement(*endOfInsertedContent.deepEquivalent().deprecatedNod e())
428 && shouldMerge(endOfInsertedContent, next); 431 && shouldMerge(endOfInsertedContent, next);
429 } 432 }
430 433
431 static bool isMailPasteAsQuotationNode(const Node* node) 434 static bool isMailPasteAsQuotationHTMLBlockQuoteElement(const Node* node)
432 { 435 {
433 if (!node || !node->hasTagName(blockquoteTag) || toElement(node)->getAttribu te(classAttr) != ApplePasteAsQuotation) 436 if (!node || !node->isHTMLElement())
437 return false;
438 const HTMLElement& element = toHTMLElement(*node);
439 if (!element.hasTagName(blockquoteTag) || element.getAttribute(classAttr) != ApplePasteAsQuotation)
434 return false; 440 return false;
435 UseCounter::count(node->document(), UseCounter::EditingApplePasteAsQuotation ); 441 UseCounter::count(node->document(), UseCounter::EditingApplePasteAsQuotation );
436 return true; 442 return true;
437 } 443 }
438 444
439 static bool isHeaderElement(const Node* a) 445 static bool isHTMLHeaderElement(const Node* a)
440 { 446 {
441 if (!a || !a->isHTMLElement()) 447 if (!a || !a->isHTMLElement())
442 return false; 448 return false;
443 449
444 const HTMLElement& element = toHTMLElement(*a); 450 const HTMLElement& element = toHTMLElement(*a);
445 return element.hasTagName(h1Tag) 451 return element.hasTagName(h1Tag)
446 || element.hasTagName(h2Tag) 452 || element.hasTagName(h2Tag)
447 || element.hasTagName(h3Tag) 453 || element.hasTagName(h3Tag)
448 || element.hasTagName(h4Tag) 454 || element.hasTagName(h4Tag)
449 || element.hasTagName(h5Tag) 455 || element.hasTagName(h5Tag)
450 || element.hasTagName(h6Tag); 456 || element.hasTagName(h6Tag);
451 } 457 }
452 458
453 static bool haveSameTagName(Node* a, Node* b) 459 static bool haveSameTagName(Element* a, Element* b)
454 { 460 {
455 return a && b && a->isElementNode() && b->isElementNode() && toElement(a)->t agName() == toElement(b)->tagName(); 461 return a && b && a->tagName() == b->tagName();
456 } 462 }
457 463
458 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V isiblePosition& destination) 464 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V isiblePosition& destination)
459 { 465 {
460 if (source.isNull() || destination.isNull()) 466 if (source.isNull() || destination.isNull())
461 return false; 467 return false;
462 468
463 Node* sourceNode = source.deepEquivalent().deprecatedNode(); 469 Node* sourceNode = source.deepEquivalent().deprecatedNode();
464 Node* destinationNode = destination.deepEquivalent().deprecatedNode(); 470 Node* destinationNode = destination.deepEquivalent().deprecatedNode();
465 Element* sourceBlock = enclosingBlock(sourceNode); 471 Element* sourceBlock = enclosingBlock(sourceNode);
466 Element* destinationBlock = enclosingBlock(destinationNode); 472 Element* destinationBlock = enclosingBlock(destinationNode);
467 return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotation Node) 473 return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotation HTMLBlockQuoteElement)
468 && sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailHTML BlockquoteElement(sourceBlock)) 474 && sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailHTML BlockquoteElement(sourceBlock))
469 && enclosingListChild(sourceBlock) == enclosingListChild(destinationNode ) 475 && enclosingListChild(sourceBlock) == enclosingListChild(destinationNode )
470 && enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(des tination.deepEquivalent()) 476 && enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(des tination.deepEquivalent())
471 && (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destin ationBlock)) 477 && (!isHTMLHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, de stinationBlock))
472 // Don't merge to or from a position before or after a block because it would 478 // Don't merge to or from a position before or after a block because it would
473 // be a no-op and cause infinite recursion. 479 // be a no-op and cause infinite recursion.
474 && !isBlock(sourceNode) && !isBlock(destinationNode); 480 && !isBlock(sourceNode) && !isBlock(destinationNode);
475 } 481 }
476 482
477 // Style rules that match just inserted elements could change their appearance, like 483 // Style rules that match just inserted elements could change their appearance, like
478 // a div inserted into a document with div { display:inline; }. 484 // a div inserted into a document with div { display:inline; }.
479 void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert edNodes& insertedNodes) 485 void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline(Insert edNodes& insertedNodes)
480 { 486 {
481 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf(); 487 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf();
(...skipping 17 matching lines...) Expand all
499 505
500 if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElem ent)) { 506 if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElem ent)) {
501 // e.g. <b style="font-weight: normal;"> is converted to <sp an style="font-weight: normal;"> 507 // e.g. <b style="font-weight: normal;"> is converted to <sp an style="font-weight: normal;">
502 element = replaceElementWithSpanPreservingChildrenAndAttribu tes(htmlElement); 508 element = replaceElementWithSpanPreservingChildrenAndAttribu tes(htmlElement);
503 inlineStyle = element->inlineStyle(); 509 inlineStyle = element->inlineStyle();
504 insertedNodes.didReplaceNode(*htmlElement, *element); 510 insertedNodes.didReplaceNode(*htmlElement, *element);
505 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttr ibutes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes, 511 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttr ibutes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes,
506 EditingStyle::DoNotExtractMatchingStyle)) { 512 EditingStyle::DoNotExtractMatchingStyle)) {
507 // e.g. <font size="3" style="font-size: 20px;"> is converte d to <font style="font-size: 20px;"> 513 // e.g. <font size="3" style="font-size: 20px;"> is converte d to <font style="font-size: 20px;">
508 for (size_t i = 0; i < attributes.size(); i++) 514 for (size_t i = 0; i < attributes.size(); i++)
509 removeNodeAttribute(element, attributes[i]); 515 removeElementAttribute(htmlElement, attributes[i]);
510 } 516 }
511 } 517 }
512 518
513 ContainerNode* context = element->parentNode(); 519 ContainerNode* context = element->parentNode();
514 520
515 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region, 521 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
516 // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>. 522 // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
517 Node* blockquoteNode = !context || isMailPasteAsQuotationNode(contex t) ? context : enclosingNodeOfType(firstPositionInNode(context), isMailHTMLBlock quoteElement, CanCrossEditingBoundary); 523 HTMLQuoteElement* blockquoteElement = !context || isMailPasteAsQuota tionHTMLBlockQuoteElement(context) ? toHTMLQuoteElement(context) : toHTMLQuoteEl ement(enclosingNodeOfType(firstPositionInNode(context), isMailHTMLBlockquoteElem ent, CanCrossEditingBoundary));
518 if (blockquoteNode) 524 if (blockquoteElement)
519 newInlineStyle->removeStyleFromRulesAndContext(element, document ().documentElement()); 525 newInlineStyle->removeStyleFromRulesAndContext(element, document ().documentElement());
520 526
521 newInlineStyle->removeStyleFromRulesAndContext(element, context); 527 newInlineStyle->removeStyleFromRulesAndContext(element, context);
522 } 528 }
523 529
524 if (!inlineStyle || newInlineStyle->isEmpty()) { 530 if (!inlineStyle || newInlineStyle->isEmpty()) {
525 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT ag(element, AllowNonEmptyStyleAttribute)) { 531 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT ag(element, AllowNonEmptyStyleAttribute)) {
526 insertedNodes.willRemoveNodePreservingChildren(*element); 532 insertedNodes.willRemoveNodePreservingChildren(*element);
527 removeNodePreservingChildren(element); 533 removeNodePreservingChildren(element);
528 continue; 534 continue;
529 } 535 }
530 removeNodeAttribute(element, styleAttr); 536 removeElementAttribute(element, styleAttr);
531 } else if (newInlineStyle->style()->propertyCount() != inlineStyle->prop ertyCount()) { 537 } else if (newInlineStyle->style()->propertyCount() != inlineStyle->prop ertyCount()) {
532 setNodeAttribute(element, styleAttr, AtomicString(newInlineStyle->st yle()->asText())); 538 setNodeAttribute(element, styleAttr, AtomicString(newInlineStyle->st yle()->asText()));
533 } 539 }
534 540
535 // FIXME: Tolerate differences in id, class, and style attributes. 541 // FIXME: Tolerate differences in id, class, and style attributes.
536 if (element->parentNode() && isNonTableCellHTMLBlockElement(element) && areIdenticalElements(element, element->parentNode()) 542 if (element->parentNode() && isNonTableCellHTMLBlockElement(element) && areIdenticalElements(element, element->parentNode())
537 && VisiblePosition(firstPositionInNode(element->parentNode())) == Vi siblePosition(firstPositionInNode(element)) 543 && VisiblePosition(firstPositionInNode(element->parentNode())) == Vi siblePosition(firstPositionInNode(element))
538 && VisiblePosition(lastPositionInNode(element->parentNode())) == Vis iblePosition(lastPositionInNode(element))) { 544 && VisiblePosition(lastPositionInNode(element->parentNode())) == Vis iblePosition(lastPositionInNode(element))) {
539 insertedNodes.willRemoveNodePreservingChildren(*element); 545 insertedNodes.willRemoveNodePreservingChildren(*element);
540 removeNodePreservingChildren(element); 546 removeNodePreservingChildren(element);
541 continue; 547 continue;
542 } 548 }
543 549
544 if (element->parentNode() && element->parentNode()->rendererIsRichlyEdit able()) 550 if (element->parentNode() && element->parentNode()->rendererIsRichlyEdit able())
545 removeNodeAttribute(element, contenteditableAttr); 551 removeElementAttribute(element, contenteditableAttr);
546 552
547 // WebKit used to not add display: inline and float: none on copy. 553 // WebKit used to not add display: inline and float: none on copy.
548 // Keep this code around for backward compatibility 554 // Keep this code around for backward compatibility
549 if (isLegacyAppleStyleSpan(element)) { 555 if (isLegacyAppleHTMLSpanElement(element)) {
550 if (!element->hasChildren()) { 556 if (!element->hasChildren()) {
551 insertedNodes.willRemoveNodePreservingChildren(*element); 557 insertedNodes.willRemoveNodePreservingChildren(*element);
552 removeNodePreservingChildren(element); 558 removeNodePreservingChildren(element);
553 continue; 559 continue;
554 } 560 }
555 // There are other styles that style rules can give to style spans, 561 // There are other styles that style rules can give to style spans,
556 // but these are the two important ones because they'll prevent 562 // but these are the two important ones because they'll prevent
557 // inserted content from appearing in the right paragraph. 563 // inserted content from appearing in the right paragraph.
558 // FIXME: Hyatt is concerned that selectively using display:inline w ill give inconsistent 564 // FIXME: Hyatt is concerned that selectively using display:inline w ill give inconsistent
559 // results. We already know one issue because td elements ignore the ir display property 565 // results. We already know one issue because td elements ignore the ir display property
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
628 void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuild er(const InsertedNodes& insertedNodes) 634 void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuild er(const InsertedNodes& insertedNodes)
629 { 635 {
630 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf(); 636 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf();
631 RefPtrWillBeRawPtr<Node> next = nullptr; 637 RefPtrWillBeRawPtr<Node> next = nullptr;
632 for (RefPtrWillBeRawPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) { 638 for (RefPtrWillBeRawPtr<Node> node = insertedNodes.firstNodeInserted(); node && node != pastEndNode; node = next) {
633 next = NodeTraversal::next(*node); 639 next = NodeTraversal::next(*node);
634 640
635 if (!node->isHTMLElement()) 641 if (!node->isHTMLElement())
636 continue; 642 continue;
637 643
638 if (isProhibitedParagraphChild(toHTMLElement(node)->localName())) { 644 HTMLElement& element = toHTMLElement(*node);
639 if (HTMLElement* paragraphElement = toHTMLElement(enclosingElementWi thTag(positionInParentBeforeNode(*node), pTag))) 645 if (isProhibitedParagraphChild(element.localName())) {
640 moveNodeOutOfAncestor(node, paragraphElement); 646 if (HTMLElement* paragraphElement = toHTMLElement(enclosingElementWi thTag(positionInParentBeforeNode(element), pTag)))
647 moveElementOutOfAncestor(&element, paragraphElement);
641 } 648 }
642 649
643 if (isHeaderElement(node.get())) { 650 if (isHTMLHeaderElement(&element)) {
644 if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeO fType(positionInParentBeforeNode(*node), isHeaderElement))) 651 if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeO fType(positionInParentBeforeNode(element), isHTMLHeaderElement)))
645 moveNodeOutOfAncestor(node, headerElement); 652 moveElementOutOfAncestor(&element, headerElement);
646 } 653 }
647 } 654 }
648 } 655 }
649 656
650 void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtrWillBeRawPtr<Node> prpNode, PassRefPtrWillBeRawPtr<ContainerNode> prpAncestor) 657 void ReplaceSelectionCommand::moveElementOutOfAncestor(PassRefPtrWillBeRawPtr<El ement> prpElement, PassRefPtrWillBeRawPtr<ContainerNode> prpAncestor)
651 { 658 {
652 RefPtrWillBeRawPtr<Node> node = prpNode; 659 RefPtrWillBeRawPtr<Element> element = prpElement;
653 RefPtrWillBeRawPtr<Node> ancestor = prpAncestor; 660 RefPtrWillBeRawPtr<ContainerNode> ancestor = prpAncestor;
654 661
655 if (!ancestor->parentNode()->hasEditableStyle()) 662 if (!ancestor->parentNode()->hasEditableStyle())
656 return; 663 return;
657 664
658 VisiblePosition positionAtEndOfNode(lastPositionInOrAfterNode(node.get())); 665 VisiblePosition positionAtEndOfNode(lastPositionInOrAfterNode(element.get()) );
659 VisiblePosition lastPositionInParagraph(lastPositionInNode(ancestor.get())); 666 VisiblePosition lastPositionInParagraph(lastPositionInNode(ancestor.get()));
660 if (positionAtEndOfNode == lastPositionInParagraph) { 667 if (positionAtEndOfNode == lastPositionInParagraph) {
661 removeNode(node); 668 removeNode(element);
662 if (ancestor->nextSibling()) 669 if (ancestor->nextSibling())
663 insertNodeBefore(node, ancestor->nextSibling()); 670 insertNodeBefore(element, ancestor->nextSibling());
664 else 671 else
665 appendNode(node, ancestor->parentNode()); 672 appendNode(element, ancestor->parentNode());
666 } else { 673 } else {
667 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(node.get(), anc estor.get(), true); 674 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(element.get(), ancestor.get(), true);
668 removeNode(node); 675 removeNode(element);
669 insertNodeBefore(node, nodeToSplitTo); 676 insertNodeBefore(element, nodeToSplitTo);
670 } 677 }
671 if (!ancestor->hasChildren()) 678 if (!ancestor->hasChildren())
672 removeNode(ancestor.release()); 679 removeNode(ancestor.release());
673 } 680 }
674 681
675 static inline bool nodeHasVisibleRenderText(Text& text) 682 static inline bool nodeHasVisibleRenderText(Text& text)
676 { 683 {
677 return text.renderer() && text.renderer()->renderedTextLength() > 0; 684 return text.renderer() && text.renderer()->renderedTextLength() > 0;
678 } 685 }
679 686
(...skipping 14 matching lines...) Expand all
694 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); 701 Node* firstNodeInserted = insertedNodes.firstNodeInserted();
695 if (firstNodeInserted && firstNodeInserted->isTextNode() && !nodeHasVisibleR enderText(toText(*firstNodeInserted))) { 702 if (firstNodeInserted && firstNodeInserted->isTextNode() && !nodeHasVisibleR enderText(toText(*firstNodeInserted))) {
696 insertedNodes.willRemoveNode(*firstNodeInserted); 703 insertedNodes.willRemoveNode(*firstNodeInserted);
697 removeNode(firstNodeInserted); 704 removeNode(firstNodeInserted);
698 } 705 }
699 } 706 }
700 707
701 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const 708 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const
702 { 709 {
703 // FIXME: Why is this hack here? What's special about <select> tags? 710 // FIXME: Why is this hack here? What's special about <select> tags?
704 Element* enclosingSelect = enclosingElementWithTag(m_endOfInsertedContent, s electTag); 711 HTMLSelectElement* enclosingSelect = toHTMLSelectElement(enclosingElementWit hTag(m_endOfInsertedContent, selectTag));
705 return VisiblePosition(enclosingSelect ? lastPositionInOrAfterNode(enclosing Select) : m_endOfInsertedContent); 712 return VisiblePosition(enclosingSelect ? lastPositionInOrAfterNode(enclosing Select) : m_endOfInsertedContent);
706 } 713 }
707 714
708 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons t 715 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons t
709 { 716 {
710 return VisiblePosition(m_startOfInsertedContent); 717 return VisiblePosition(m_startOfInsertedContent);
711 } 718 }
712 719
713 static void removeHeadContents(ReplacementFragment& fragment) 720 static void removeHeadContents(ReplacementFragment& fragment)
714 { 721 {
(...skipping 13 matching lines...) Expand all
728 } 735 }
729 736
730 // Remove style spans before insertion if they are unnecessary. It's faster bec ause we'll 737 // Remove style spans before insertion if they are unnecessary. It's faster bec ause we'll
731 // avoid doing a layout. 738 // avoid doing a layout.
732 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos) 739 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
733 { 740 {
734 Node* topNode = fragment.firstChild(); 741 Node* topNode = fragment.firstChild();
735 742
736 // Handling the case where we are doing Paste as Quotation or pasting into q uoted content is more complicated (see handleStyleSpans) 743 // Handling the case where we are doing Paste as Quotation or pasting into q uoted content is more complicated (see handleStyleSpans)
737 // and doesn't receive the optimization. 744 // and doesn't receive the optimization.
738 if (isMailPasteAsQuotationNode(topNode) || enclosingNodeOfType(firstPosition InOrBeforeNode(topNode), isMailHTMLBlockquoteElement, CanCrossEditingBoundary)) 745 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || enclosingNodeOfT ype(firstPositionInOrBeforeNode(topNode), isMailHTMLBlockquoteElement, CanCrossE ditingBoundary))
739 return false; 746 return false;
740 747
741 // Either there are no style spans in the fragment or a WebKit client has ad ded content to the fragment 748 // Either there are no style spans in the fragment or a WebKit client has ad ded content to the fragment
742 // before inserting it. Look for and handle style spans after insertion. 749 // before inserting it. Look for and handle style spans after insertion.
743 if (!isLegacyAppleStyleSpan(topNode)) 750 if (!isLegacyAppleHTMLSpanElement(topNode))
744 return false; 751 return false;
745 752
746 Node* wrappingStyleSpan = topNode; 753 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode);
747 RefPtrWillBeRawPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create( insertionPos.parentAnchoredEquivalent()); 754 RefPtrWillBeRawPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create( insertionPos.parentAnchoredEquivalent());
748 String styleText = styleAtInsertionPos->style()->asText(); 755 String styleText = styleAtInsertionPos->style()->asText();
749 756
750 // FIXME: This string comparison is a naive way of comparing two styles. 757 // FIXME: This string comparison is a naive way of comparing two styles.
751 // We should be taking the diff and check that the diff is empty. 758 // We should be taking the diff and check that the diff is empty.
752 if (styleText != toElement(wrappingStyleSpan)->getAttribute(styleAttr)) 759 if (styleText != wrappingStyleSpan->getAttribute(styleAttr))
753 return false; 760 return false;
754 761
755 fragment.removeNodePreservingChildren(wrappingStyleSpan); 762 fragment.removeNodePreservingChildren(wrappingStyleSpan);
756 return true; 763 return true;
757 } 764 }
758 765
759 // At copy time, WebKit wraps copied content in a span that contains the source document's 766 // At copy time, WebKit wraps copied content in a span that contains the source document's
760 // default styles. If the copied Range inherits any other styles from its ances tors, we put 767 // default styles. If the copied Range inherits any other styles from its ances tors, we put
761 // those styles on a second span. 768 // those styles on a second span.
762 // This function removes redundant styles from those spans, and removes the span s if all their 769 // This function removes redundant styles from those spans, and removes the span s if all their
763 // styles are redundant. 770 // styles are redundant.
764 // We should remove the Apple-style-span class when we're done, see <rdar://prob lem/5685600>. 771 // We should remove the Apple-style-span class when we're done, see <rdar://prob lem/5685600>.
765 // We should remove styles from spans that are overridden by all of their childr en, either here 772 // We should remove styles from spans that are overridden by all of their childr en, either here
766 // or at copy time. 773 // or at copy time.
767 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes) 774 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes)
768 { 775 {
769 HTMLElement* wrappingStyleSpan = 0; 776 HTMLSpanElement* wrappingStyleSpan = 0;
770 // The style span that contains the source document's default style should b e at 777 // The style span that contains the source document's default style should b e at
771 // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation), 778 // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation),
772 // so search for the top level style span instead of assuming it's at the to p. 779 // so search for the top level style span instead of assuming it's at the to p.
773 for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraver sal::next(*node)) { 780 for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraver sal::next(*node)) {
774 if (isLegacyAppleStyleSpan(node)) { 781 if (isLegacyAppleHTMLSpanElement(node)) {
775 wrappingStyleSpan = toHTMLElement(node); 782 wrappingStyleSpan = toHTMLSpanElement(node);
776 break; 783 break;
777 } 784 }
778 } 785 }
779 786
780 // There might not be any style spans if we're pasting from another applicat ion or if 787 // There might not be any style spans if we're pasting from another applicat ion or if
781 // we are here because of a document.execCommand("InsertHTML", ...) call. 788 // we are here because of a document.execCommand("InsertHTML", ...) call.
782 if (!wrappingStyleSpan) 789 if (!wrappingStyleSpan)
783 return; 790 return;
784 791
785 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(wrappingStyleS pan->inlineStyle()); 792 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(wrappingStyleS pan->inlineStyle());
786 ContainerNode* context = wrappingStyleSpan->parentNode(); 793 ContainerNode* context = wrappingStyleSpan->parentNode();
787 794
788 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if yo u're pasting into a quoted region, 795 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if yo u're pasting into a quoted region,
789 // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>. 796 // styles from blockquoteElement are allowed to override those from the sour ce document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
790 Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : enclo singNodeOfType(firstPositionInNode(context), isMailHTMLBlockquoteElement, CanCro ssEditingBoundary); 797 HTMLQuoteElement* blockquoteElement = isMailPasteAsQuotationHTMLBlockQuoteEl ement(context) ? toHTMLQuoteElement(context) : toHTMLQuoteElement(enclosingNodeO fType(firstPositionInNode(context), isMailHTMLBlockquoteElement, CanCrossEditing Boundary));
791 if (blockquoteNode) 798 if (blockquoteElement)
792 context = document().documentElement(); 799 context = document().documentElement();
793 800
794 // This operation requires that only editing styles to be removed from sourc eDocumentStyle. 801 // This operation requires that only editing styles to be removed from sourc eDocumentStyle.
795 style->prepareToApplyAt(firstPositionInNode(context)); 802 style->prepareToApplyAt(firstPositionInNode(context));
796 803
797 // Remove block properties in the span's style. This prevents properties tha t probably have no effect 804 // Remove block properties in the span's style. This prevents properties tha t probably have no effect
798 // currently from affecting blocks later if the style is cloned for a new bl ock element during a future 805 // currently from affecting blocks later if the style is cloned for a new bl ock element during a future
799 // editing operation. 806 // editing operation.
800 // FIXME: They *can* have an effect currently if blocks beneath the style sp an aren't individually marked 807 // FIXME: They *can* have an effect currently if blocks beneath the style sp an aren't individually marked
801 // with block styles by the editing engine used to style them. WebKit doesn 't do this, but others might. 808 // with block styles by the editing engine used to style them. WebKit doesn 't do this, but others might.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 // Stop if any previous sibling is a block. 868 // Stop if any previous sibling is a block.
862 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling ->previousSibling()) { 869 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling ->previousSibling()) {
863 if (isBlockFlowElement(*sibling)) 870 if (isBlockFlowElement(*sibling))
864 return node; 871 return node;
865 } 872 }
866 node = parent; 873 node = parent;
867 } 874 }
868 return node; 875 return node;
869 } 876 }
870 877
871 static bool isInlineNodeWithStyle(const Node* node) 878 static bool isInlineHTMLElementWithStyle(const Node* node)
872 { 879 {
873 // We don't want to skip over any block elements. 880 // We don't want to skip over any block elements.
874 if (isBlock(node)) 881 if (isBlock(node))
875 return false; 882 return false;
876 883
877 if (!node->isHTMLElement()) 884 if (!node->isHTMLElement())
878 return false; 885 return false;
879 886
880 // We can skip over elements whose class attribute is 887 // We can skip over elements whose class attribute is
881 // one of our internal classes. 888 // one of our internal classes.
882 const HTMLElement* element = toHTMLElement(node); 889 const HTMLElement* element = toHTMLElement(node);
883 const AtomicString& classAttributeValue = element->getAttribute(classAttr); 890 const AtomicString& classAttributeValue = element->getAttribute(classAttr);
884 if (classAttributeValue == AppleTabSpanClass) { 891 if (classAttributeValue == AppleTabSpanClass) {
885 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass ); 892 UseCounter::count(element->document(), UseCounter::EditingAppleTabSpanCl ass);
886 return true; 893 return true;
887 } 894 }
888 if (classAttributeValue == AppleConvertedSpace) { 895 if (classAttributeValue == AppleConvertedSpace) {
889 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpa ce); 896 UseCounter::count(element->document(), UseCounter::EditingAppleConverted Space);
890 return true; 897 return true;
891 } 898 }
892 if (classAttributeValue == ApplePasteAsQuotation) { 899 if (classAttributeValue == ApplePasteAsQuotation) {
893 UseCounter::count(node->document(), UseCounter::EditingApplePasteAsQuota tion); 900 UseCounter::count(element->document(), UseCounter::EditingApplePasteAsQu otation);
894 return true; 901 return true;
895 } 902 }
896 903
897 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element); 904 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element);
898 } 905 }
899 906
900 inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& i nsertionPos) 907 static inline HTMLElement* elementToSplitToAvoidPastingIntoInlineElementsWithSty le(const Position& insertionPos)
901 { 908 {
902 Element* containingBlock = enclosingBlock(insertionPos.containerNode()); 909 Element* containingBlock = enclosingBlock(insertionPos.containerNode());
903 return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, Canno tCrossEditingBoundary, containingBlock); 910 return toHTMLElement(highestEnclosingNodeOfType(insertionPos, isInlineHTMLEl ementWithStyle, CannotCrossEditingBoundary, containingBlock));
904 } 911 }
905 912
906 void ReplaceSelectionCommand::doApply() 913 void ReplaceSelectionCommand::doApply()
907 { 914 {
908 VisibleSelection selection = endingSelection(); 915 VisibleSelection selection = endingSelection();
909 ASSERT(selection.isCaretOrRange()); 916 ASSERT(selection.isCaretOrRange());
910 ASSERT(selection.start().deprecatedNode()); 917 ASSERT(selection.start().deprecatedNode());
911 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedN ode()) 918 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedN ode())
912 return; 919 return;
913 920
(...skipping 13 matching lines...) Expand all
927 m_insertionStyle = EditingStyle::create(selection.start()); 934 m_insertionStyle = EditingStyle::create(selection.start());
928 m_insertionStyle->mergeTypingStyle(&document()); 935 m_insertionStyle->mergeTypingStyle(&document());
929 } 936 }
930 937
931 VisiblePosition visibleStart = selection.visibleStart(); 938 VisiblePosition visibleStart = selection.visibleStart();
932 VisiblePosition visibleEnd = selection.visibleEnd(); 939 VisiblePosition visibleEnd = selection.visibleEnd();
933 940
934 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); 941 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd);
935 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); 942 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart);
936 943
937 Node* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEquival ent().deprecatedNode()); 944 Element* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEqui valent().deprecatedNode());
938 945
939 Position insertionPos = selection.start(); 946 Position insertionPos = selection.start();
940 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailH TMLBlockquoteElement, CanCrossEditingBoundary); 947 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailH TMLBlockquoteElement, CanCrossEditingBoundary);
941 bool selectionIsPlainText = !selection.isContentRichlyEditable(); 948 bool selectionIsPlainText = !selection.isContentRichlyEditable();
942 Element* currentRoot = selection.rootEditableElement(); 949 Element* currentRoot = selection.rootEditableElement();
943 950
944 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && ! startIsInsideMailBlockquote) || 951 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && ! startIsInsideMailBlockquote) ||
945 enclosingBlockOfVisibleStart == currentRoot || isListItem(enclosingBlock OfVisibleStart) || selectionIsPlainText) 952 enclosingBlockOfVisibleStart == currentRoot || isListItem(enclosingBlock OfVisibleStart) || selectionIsPlainText)
946 m_preventNesting = false; 953 m_preventNesting = false;
947 954
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1004 // Inserting content could cause whitespace to collapse, e.g. inserting <div >foo</div> into hello^ world. 1011 // Inserting content could cause whitespace to collapse, e.g. inserting <div >foo</div> into hello^ world.
1005 prepareWhitespaceAtPositionForSplit(insertionPos); 1012 prepareWhitespaceAtPositionForSplit(insertionPos);
1006 1013
1007 // If the downstream node has been removed there's no point in continuing. 1014 // If the downstream node has been removed there's no point in continuing.
1008 if (!insertionPos.downstream().deprecatedNode()) 1015 if (!insertionPos.downstream().deprecatedNode())
1009 return; 1016 return;
1010 1017
1011 // NOTE: This would be an incorrect usage of downstream() if downstream() we re changed to mean the last position after 1018 // NOTE: This would be an incorrect usage of downstream() if downstream() we re changed to mean the last position after
1012 // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed 1019 // p that maps to the same visible position as p (since in the case where a br is at the end of a block and collapsed
1013 // away, there are positions after the br which map to the same visible posi tion as [br, 0]). 1020 // away, there are positions after the br which map to the same visible posi tion as [br, 0]).
1014 Node* endBR = isHTMLBRElement(*insertionPos.downstream().deprecatedNode()) ? insertionPos.downstream().deprecatedNode() : 0; 1021 HTMLBRElement* endBR = isHTMLBRElement(*insertionPos.downstream().deprecated Node()) ? toHTMLBRElement(insertionPos.downstream().deprecatedNode()) : 0;
1015 VisiblePosition originalVisPosBeforeEndBR; 1022 VisiblePosition originalVisPosBeforeEndBR;
1016 if (endBR) 1023 if (endBR)
1017 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D OWNSTREAM).previous(); 1024 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D OWNSTREAM).previous();
1018 1025
1019 RefPtrWillBeRawPtr<Node> enclosingBlockOfInsertionPos = enclosingBlock(inser tionPos.deprecatedNode()); 1026 RefPtrWillBeRawPtr<Element> enclosingBlockOfInsertionPos = enclosingBlock(in sertionPos.deprecatedNode());
1020 1027
1021 // Adjust insertionPos to prevent nesting. 1028 // Adjust insertionPos to prevent nesting.
1022 // If the start was in a Mail blockquote, we will have already handled adjus ting insertionPos above. 1029 // If the start was in a Mail blockquote, we will have already handled adjus ting insertionPos above.
1023 if (m_preventNesting && enclosingBlockOfInsertionPos && !isTableCell(enclosi ngBlockOfInsertionPos.get()) && !startIsInsideMailBlockquote) { 1030 if (m_preventNesting && enclosingBlockOfInsertionPos && !isTableCell(enclosi ngBlockOfInsertionPos.get()) && !startIsInsideMailBlockquote) {
1024 ASSERT(enclosingBlockOfInsertionPos != currentRoot); 1031 ASSERT(enclosingBlockOfInsertionPos != currentRoot);
1025 VisiblePosition visibleInsertionPos(insertionPos); 1032 VisiblePosition visibleInsertionPos(insertionPos);
1026 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti onPos) && fragment.hasInterchangeNewlineAtEnd())) 1033 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti onPos) && fragment.hasInterchangeNewlineAtEnd()))
1027 insertionPos = positionInParentAfterNode(*enclosingBlockOfInsertionP os); 1034 insertionPos = positionInParentAfterNode(*enclosingBlockOfInsertionP os);
1028 else if (isStartOfBlock(visibleInsertionPos)) 1035 else if (isStartOfBlock(visibleInsertionPos))
1029 insertionPos = positionInParentBeforeNode(*enclosingBlockOfInsertion Pos); 1036 insertionPos = positionInParentBeforeNode(*enclosingBlockOfInsertion Pos);
(...skipping 28 matching lines...) Expand all
1058 // This way we can produce a less verbose markup. 1065 // This way we can produce a less verbose markup.
1059 // We can skip this optimization for fragments not wrapped in one of 1066 // We can skip this optimization for fragments not wrapped in one of
1060 // our style spans and for positions inside list items 1067 // our style spans and for positions inside list items
1061 // since insertAsListItems already does the right thing. 1068 // since insertAsListItems already does the right thing.
1062 if (!m_matchStyle && !enclosingList(insertionPos.containerNode())) { 1069 if (!m_matchStyle && !enclosingList(insertionPos.containerNode())) {
1063 if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInC ontainerNode() && !insertionPos.atLastEditingPositionForNode()) { 1070 if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInC ontainerNode() && !insertionPos.atLastEditingPositionForNode()) {
1064 splitTextNode(insertionPos.containerText(), insertionPos.offsetInCon tainerNode()); 1071 splitTextNode(insertionPos.containerText(), insertionPos.offsetInCon tainerNode());
1065 insertionPos = firstPositionInNode(insertionPos.containerNode()); 1072 insertionPos = firstPositionInNode(insertionPos.containerNode());
1066 } 1073 }
1067 1074
1068 if (RefPtrWillBeRawPtr<Node> nodeToSplitTo = nodeToSplitToAvoidPastingIn toInlineNodesWithStyle(insertionPos)) { 1075 if (RefPtrWillBeRawPtr<HTMLElement> elementToSplitTo = elementToSplitToA voidPastingIntoInlineElementsWithStyle(insertionPos)) {
1069 if (insertionPos.containerNode() != nodeToSplitTo->parentNode()) { 1076 if (insertionPos.containerNode() != elementToSplitTo->parentNode()) {
1070 Node* splitStart = insertionPos.computeNodeAfterPosition(); 1077 Node* splitStart = insertionPos.computeNodeAfterPosition();
1071 if (!splitStart) 1078 if (!splitStart)
1072 splitStart = insertionPos.containerNode(); 1079 splitStart = insertionPos.containerNode();
1073 nodeToSplitTo = splitTreeToNode(splitStart, nodeToSplitTo->paren tNode()).get(); 1080 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(splitSt art, elementToSplitTo->parentNode()).get();
1074 insertionPos = positionInParentBeforeNode(*nodeToSplitTo); 1081 insertionPos = positionInParentBeforeNode(*nodeToSplitTo);
1075 } 1082 }
1076 } 1083 }
1077 } 1084 }
1078 1085
1079 // FIXME: When pasting rich content we're often prevented from heading down the fast path by style spans. Try 1086 // FIXME: When pasting rich content we're often prevented from heading down the fast path by style spans. Try
1080 // again here if they've been removed. 1087 // again here if they've been removed.
1081 1088
1082 // 1) Insert the content. 1089 // 1) Insert the content.
1083 // 2) Remove redundant styles and style tags, this inner <b> for example: <b >foo <b>bar</b> baz</b>. 1090 // 2) Remove redundant styles and style tags, this inner <b> for example: <b >foo <b>bar</b> baz</b>.
1084 // 3) Merge the start of the added content with the content before the posit ion being pasted into. 1091 // 3) Merge the start of the added content with the content before the posit ion being pasted into.
1085 // 4) Do one of the following: a) expand the last br if the fragment ends wi th one and it collapsed, 1092 // 4) Do one of the following: a) expand the last br if the fragment ends wi th one and it collapsed,
1086 // b) merge the last paragraph of the incoming fragment with the paragraph t hat contained the 1093 // b) merge the last paragraph of the incoming fragment with the paragraph t hat contained the
1087 // end of the selection that was pasted into, or c) handle an interchange ne wline at the end of the 1094 // end of the selection that was pasted into, or c) handle an interchange ne wline at the end of the
1088 // incoming fragment. 1095 // incoming fragment.
1089 // 5) Add spaces for smart replace. 1096 // 5) Add spaces for smart replace.
1090 // 6) Select the replacement if requested, and match style if requested. 1097 // 6) Select the replacement if requested, and match style if requested.
1091 1098
1092 InsertedNodes insertedNodes; 1099 InsertedNodes insertedNodes;
1093 RefPtrWillBeRawPtr<Node> refNode = fragment.firstChild(); 1100 RefPtrWillBeRawPtr<Node> refNode = fragment.firstChild();
1094 ASSERT(refNode); 1101 ASSERT(refNode);
1095 RefPtrWillBeRawPtr<Node> node = refNode->nextSibling(); 1102 RefPtrWillBeRawPtr<Node> node = refNode->nextSibling();
1096 1103
1097 fragment.removeNode(refNode); 1104 fragment.removeNode(refNode);
1098 1105
1099 Node* blockStart = enclosingBlock(insertionPos.deprecatedNode()); 1106 Element* blockStart = enclosingBlock(insertionPos.deprecatedNode());
1100 if ((isHTMLListElement(refNode.get()) || (isLegacyAppleStyleSpan(refNode.get ()) && isHTMLListElement(refNode->firstChild()))) 1107 if ((isHTMLListElement(refNode.get()) || (isLegacyAppleHTMLSpanElement(refNo de.get()) && isHTMLListElement(refNode->firstChild())))
1101 && blockStart && blockStart->renderer()->isListItem()) 1108 && blockStart && blockStart->renderer()->isListItem())
1102 refNode = insertAsListItems(toHTMLElement(refNode), blockStart, insertio nPos, insertedNodes); 1109 refNode = insertAsListItems(toHTMLElement(refNode), blockStart, insertio nPos, insertedNodes);
1103 else { 1110 else {
1104 insertNodeAt(refNode, insertionPos); 1111 insertNodeAt(refNode, insertionPos);
1105 insertedNodes.respondToNodeInsertion(*refNode); 1112 insertedNodes.respondToNodeInsertion(*refNode);
1106 } 1113 }
1107 1114
1108 // Mutation events (bug 22634) may have already removed the inserted content 1115 // Mutation events (bug 22634) may have already removed the inserted content
1109 if (!refNode->inDocument()) 1116 if (!refNode->inDocument())
1110 return; 1117 return;
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after
1207 } 1214 }
1208 1215
1209 Position lastPositionToSelect; 1216 Position lastPositionToSelect;
1210 if (fragment.hasInterchangeNewlineAtEnd()) { 1217 if (fragment.hasInterchangeNewlineAtEnd()) {
1211 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); 1218 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent();
1212 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound ary); 1219 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound ary);
1213 1220
1214 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont ent) || next.isNull()) { 1221 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont ent) || next.isNull()) {
1215 if (!isStartOfParagraph(endOfInsertedContent)) { 1222 if (!isStartOfParagraph(endOfInsertedContent)) {
1216 setEndingSelection(endOfInsertedContent); 1223 setEndingSelection(endOfInsertedContent);
1217 Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEq uivalent().deprecatedNode()); 1224 Element* enclosingBlockElement = enclosingBlock(endOfInsertedCon tent.deepEquivalent().deprecatedNode());
1218 if (isListItem(enclosingNode)) { 1225 if (isListItem(enclosingBlockElement)) {
1219 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt emElement(document()); 1226 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt emElement(document());
1220 insertNodeAfter(newListItem, enclosingNode); 1227 insertNodeAfter(newListItem, enclosingBlockElement);
1221 setEndingSelection(VisiblePosition(firstPositionInNode(newLi stItem.get()))); 1228 setEndingSelection(VisiblePosition(firstPositionInNode(newLi stItem.get())));
1222 } else { 1229 } else {
1223 // Use a default paragraph element (a plain div) for the emp ty paragraph, using the last paragraph 1230 // Use a default paragraph element (a plain div) for the emp ty paragraph, using the last paragraph
1224 // block's style seems to annoy users. 1231 // block's style seems to annoy users.
1225 insertParagraphSeparator(true, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), 1232 insertParagraphSeparator(true, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(),
1226 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary, insertedNodes.firstNodeInserted()->parentNode())); 1233 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary, insertedNodes.firstNodeInserted()->parentNode()));
1227 } 1234 }
1228 1235
1229 // Select up to the paragraph separator that was added. 1236 // Select up to the paragraph separator that was added.
1230 lastPositionToSelect = endingSelection().visibleStart().deepEqui valent(); 1237 lastPositionToSelect = endingSelection().visibleStart().deepEqui valent();
1231 updateNodesInserted(lastPositionToSelect.deprecatedNode()); 1238 updateNodesInserted(lastPositionToSelect.deprecatedNode());
1232 } 1239 }
1233 } else { 1240 } else {
1234 // Select up to the beginning of the next paragraph. 1241 // Select up to the beginning of the next paragraph.
1235 lastPositionToSelect = next.deepEquivalent().downstream(); 1242 lastPositionToSelect = next.deepEquivalent().downstream();
1236 } 1243 }
1244 } else {
1245 mergeEndIfNeeded();
1246 }
1237 1247
1238 } else 1248 if (HTMLQuoteElement* mailBlockquote = toHTMLQuoteElement(enclosingNodeOfTyp e(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationHTM LBlockQuoteElement)))
1239 mergeEndIfNeeded(); 1249 removeElementAttribute(mailBlockquote, classAttr);
1240
1241 if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedCont ent().deepEquivalent(), isMailPasteAsQuotationNode))
1242 removeNodeAttribute(toElement(mailBlockquote), classAttr);
1243 1250
1244 if (shouldPerformSmartReplace()) 1251 if (shouldPerformSmartReplace())
1245 addSpacesForSmartReplace(); 1252 addSpacesForSmartReplace();
1246 1253
1247 // If we are dealing with a fragment created from plain text 1254 // If we are dealing with a fragment created from plain text
1248 // no style matching is necessary. 1255 // no style matching is necessary.
1249 if (plainTextFragment) 1256 if (plainTextFragment)
1250 m_matchStyle = false; 1257 m_matchStyle = false;
1251 1258
1252 completeHTMLReplacement(lastPositionToSelect); 1259 completeHTMLReplacement(lastPositionToSelect);
1253 } 1260 }
1254 1261
1255 bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePositi on& originalVisPosBeforeEndBR) 1262 bool ReplaceSelectionCommand::shouldRemoveEndBR(HTMLBRElement* endBR, const Visi blePosition& originalVisPosBeforeEndBR)
1256 { 1263 {
1257 if (!endBR || !endBR->inDocument()) 1264 if (!endBR || !endBR->inDocument())
1258 return false; 1265 return false;
1259 1266
1260 VisiblePosition visiblePos(positionBeforeNode(endBR)); 1267 VisiblePosition visiblePos(positionBeforeNode(endBR));
1261 1268
1262 // Don't remove the br if nothing was inserted. 1269 // Don't remove the br if nothing was inserted.
1263 if (visiblePos.previous() == originalVisPosBeforeEndBR) 1270 if (visiblePos.previous() == originalVisPosBeforeEndBR)
1264 return false; 1271 return false;
1265 1272
1266 // Remove the br if it is collapsed away and so is unnecessary. 1273 // Remove the br if it is collapsed away and so is unnecessary.
1267 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && !isStartOfPa ragraph(visiblePos)) 1274 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && !isStartOfPa ragraph(visiblePos))
1268 return true; 1275 return true;
1269 1276
1270 // A br that was originally holding a line open should be displaced by inser ted content or turned into a line break. 1277 // A br that was originally holding a line open should be displaced by inser ted content or turned into a line break.
1271 // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder. 1278 // A br that was originally acting as a line break should still be acting as a line break, not as a placeholder.
1272 return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos); 1279 return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos);
1273 } 1280 }
1274 1281
1275 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const 1282 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const
1276 { 1283 {
1277 if (!m_smartReplace) 1284 if (!m_smartReplace)
1278 return false; 1285 return false;
1279 1286
1280 Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedCon tent().deepEquivalent()); 1287 HTMLTextFormControlElement* textControl = enclosingTextFormControl(positionA tStartOfInsertedContent().deepEquivalent());
1281 if (isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPa sswordField()) 1288 if (isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPa sswordField())
1282 return false; // Disable smart replace for password fields. 1289 return false; // Disable smart replace for password fields.
1283 1290
1284 return true; 1291 return true;
1285 } 1292 }
1286 1293
1287 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha racter, bool previousCharacter) 1294 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha racter, bool previousCharacter)
1288 { 1295 {
1289 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara cter, previousCharacter); 1296 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara cter, previousCharacter);
1290 } 1297 }
(...skipping 12 matching lines...) Expand all
1303 } 1310 }
1304 1311
1305 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte rAfter(), false); 1312 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte rAfter(), false);
1306 if (needsTrailingSpace && endNode) { 1313 if (needsTrailingSpace && endNode) {
1307 bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->s tyle()->collapseWhiteSpace(); 1314 bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->s tyle()->collapseWhiteSpace();
1308 if (endNode->isTextNode()) { 1315 if (endNode->isTextNode()) {
1309 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1316 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ? nonBreakingSpaceString() : " ");
1310 if (m_endOfInsertedContent.containerNode() == endNode) 1317 if (m_endOfInsertedContent.containerNode() == endNode)
1311 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse tInContainerNode() + 1); 1318 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse tInContainerNode() + 1);
1312 } else { 1319 } else {
1313 RefPtrWillBeRawPtr<Node> node = document().createEditingTextNode(col lapseWhiteSpace ? nonBreakingSpaceString() : " "); 1320 RefPtrWillBeRawPtr<Text> node = document().createEditingTextNode(col lapseWhiteSpace ? nonBreakingSpaceString() : " ");
1314 insertNodeAfter(node, endNode); 1321 insertNodeAfter(node, endNode);
1315 updateNodesInserted(node.get()); 1322 updateNodesInserted(node.get());
1316 } 1323 }
1317 } 1324 }
1318 1325
1319 document().updateLayout(); 1326 document().updateLayout();
1320 1327
1321 Position startDownstream = startOfInsertedContent.deepEquivalent().downstrea m(); 1328 Position startDownstream = startOfInsertedContent.deepEquivalent().downstrea m();
1322 Node* startNode = startDownstream.computeNodeAfterPosition(); 1329 Node* startNode = startDownstream.computeNodeAfterPosition();
1323 unsigned startOffset = 0; 1330 unsigned startOffset = 0;
1324 if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) { 1331 if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) {
1325 startNode = startDownstream.containerNode(); 1332 startNode = startDownstream.containerNode();
1326 startOffset = startDownstream.offsetInContainerNode(); 1333 startOffset = startDownstream.offsetInContainerNode();
1327 } 1334 }
1328 1335
1329 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre vious().characterAfter(), true); 1336 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre vious().characterAfter(), true);
1330 if (needsLeadingSpace && startNode) { 1337 if (needsLeadingSpace && startNode) {
1331 bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer( )->style()->collapseWhiteSpace(); 1338 bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer( )->style()->collapseWhiteSpace();
1332 if (startNode->isTextNode()) { 1339 if (startNode->isTextNode()) {
1333 insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpac e ? nonBreakingSpaceString() : " "); 1340 insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpac e ? nonBreakingSpaceString() : " ");
1334 if (m_endOfInsertedContent.containerNode() == startNode && m_endOfIn sertedContent.offsetInContainerNode()) 1341 if (m_endOfInsertedContent.containerNode() == startNode && m_endOfIn sertedContent.offsetInContainerNode())
1335 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse tInContainerNode() + 1); 1342 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse tInContainerNode() + 1);
1336 } else { 1343 } else {
1337 RefPtrWillBeRawPtr<Node> node = document().createEditingTextNode(col lapseWhiteSpace ? nonBreakingSpaceString() : " "); 1344 RefPtrWillBeRawPtr<Text> node = document().createEditingTextNode(col lapseWhiteSpace ? nonBreakingSpaceString() : " ");
1338 // Don't updateNodesInserted. Doing so would set m_endOfInsertedCont ent to be the node containing the leading space, 1345 // Don't updateNodesInserted. Doing so would set m_endOfInsertedCont ent to be the node containing the leading space,
1339 // but m_endOfInsertedContent is supposed to mark the end of pasted content. 1346 // but m_endOfInsertedContent is supposed to mark the end of pasted content.
1340 insertNodeBefore(node, startNode); 1347 insertNodeBefore(node, startNode);
1341 m_startOfInsertedContent = firstPositionInNode(node.get()); 1348 m_startOfInsertedContent = firstPositionInNode(node.get());
1342 } 1349 }
1343 } 1350 }
1344 } 1351 }
1345 1352
1346 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi onToSelect) 1353 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi onToSelect)
1347 { 1354 {
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
1431 } 1438 }
1432 } 1439 }
1433 1440
1434 EditAction ReplaceSelectionCommand::editingAction() const 1441 EditAction ReplaceSelectionCommand::editingAction() const
1435 { 1442 {
1436 return m_editAction; 1443 return m_editAction;
1437 } 1444 }
1438 1445
1439 // If the user is inserting a list into an existing list, instead of nesting the list, 1446 // If the user is inserting a list into an existing list, instead of nesting the list,
1440 // we put the list items into the existing list. 1447 // we put the list items into the existing list.
1441 Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtrWillBeRawPtr<HTMLElem ent> prpListElement, Node* insertionBlock, const Position& insertPos, InsertedNo des& insertedNodes) 1448 Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtrWillBeRawPtr<HTMLElem ent> prpListElement, Element* insertionBlock, const Position& insertPos, Inserte dNodes& insertedNodes)
1442 { 1449 {
1443 RefPtrWillBeRawPtr<HTMLElement> listElement = prpListElement; 1450 RefPtrWillBeRawPtr<HTMLElement> listElement = prpListElement;
1444 1451
1445 while (listElement->hasOneChild() && isHTMLListElement(listElement->firstChi ld())) 1452 while (listElement->hasOneChild() && isHTMLListElement(listElement->firstChi ld()))
1446 listElement = toHTMLElement(listElement->firstChild()); 1453 listElement = toHTMLElement(listElement->firstChild());
1447 1454
1448 bool isStart = isStartOfParagraph(VisiblePosition(insertPos)); 1455 bool isStart = isStartOfParagraph(VisiblePosition(insertPos));
1449 bool isEnd = isEndOfParagraph(VisiblePosition(insertPos)); 1456 bool isEnd = isEndOfParagraph(VisiblePosition(insertPos));
1450 bool isMiddle = !isStart && !isEnd; 1457 bool isMiddle = !isStart && !isEnd;
1451 Node* lastNode = insertionBlock; 1458 Node* lastNode = insertionBlock;
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1496 bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f ragment) 1503 bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f ragment)
1497 { 1504 {
1498 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || !fragment.firstChild()->isTextNode()) 1505 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || !fragment.firstChild()->isTextNode())
1499 return false; 1506 return false;
1500 1507
1501 // FIXME: Would be nice to handle smart replace in the fast path. 1508 // FIXME: Would be nice to handle smart replace in the fast path.
1502 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha sInterchangeNewlineAtEnd()) 1509 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha sInterchangeNewlineAtEnd())
1503 return false; 1510 return false;
1504 1511
1505 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" s hould not be underlined. 1512 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" s hould not be underlined.
1506 if (nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(endingSelection().star t())) 1513 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle(endingSelection( ).start()))
1507 return false; 1514 return false;
1508 1515
1509 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = endingSelection().end().dow nstream().anchorNode(); 1516 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = endingSelection().end().dow nstream().anchorNode();
1510 Text* textNode = toText(fragment.firstChild()); 1517 Text* textNode = toText(fragment.firstChild());
1511 // Our fragment creation code handles tabs, spaces, and newlines, so we don' t have to worry about those here. 1518 // Our fragment creation code handles tabs, spaces, and newlines, so we don' t have to worry about those here.
1512 1519
1513 Position start = endingSelection().start(); 1520 Position start = endingSelection().start();
1514 Position end = replaceSelectedTextInNode(textNode->data()); 1521 Position end = replaceSelectedTextInNode(textNode->data());
1515 if (end.isNull()) 1522 if (end.isNull())
1516 return false; 1523 return false;
1517 1524
1518 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR Element(*nodeAfterInsertionPos) 1525 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR Element(*nodeAfterInsertionPos)
1519 && shouldRemoveEndBR(nodeAfterInsertionPos.get(), VisiblePosition(positi onBeforeNode(nodeAfterInsertionPos.get())))) 1526 && shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos.get()), Visib lePosition(positionBeforeNode(nodeAfterInsertionPos.get()))))
1520 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); 1527 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get());
1521 1528
1522 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en d); 1529 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en d);
1523 1530
1524 setEndingSelection(selectionAfterReplace); 1531 setEndingSelection(selectionAfterReplace);
1525 1532
1526 return true; 1533 return true;
1527 } 1534 }
1528 1535
1529 void ReplaceSelectionCommand::trace(Visitor* visitor) 1536 void ReplaceSelectionCommand::trace(Visitor* visitor)
1530 { 1537 {
1531 visitor->trace(m_startOfInsertedContent); 1538 visitor->trace(m_startOfInsertedContent);
1532 visitor->trace(m_endOfInsertedContent); 1539 visitor->trace(m_endOfInsertedContent);
1533 visitor->trace(m_insertionStyle); 1540 visitor->trace(m_insertionStyle);
1534 visitor->trace(m_documentFragment); 1541 visitor->trace(m_documentFragment);
1535 CompositeEditCommand::trace(visitor); 1542 CompositeEditCommand::trace(visitor);
1536 } 1543 }
1537 1544
1538 } // namespace blink 1545 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/editing/ReplaceSelectionCommand.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698