OLD | NEW |
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 Loading... |
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(Element*); |
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 Loading... |
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 Loading... |
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(Element*
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 Loading... |
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 Loading... |
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) ? |
518 if (blockquoteNode) | 524 toHTMLQuoteElement(context) : |
| 525 toHTMLQuoteElement(enclosingNodeOfType(firstPositionInNode(conte
xt), isMailHTMLBlockquoteElement, CanCrossEditingBoundary)); |
| 526 if (blockquoteElement) |
519 newInlineStyle->removeStyleFromRulesAndContext(element, document
().documentElement()); | 527 newInlineStyle->removeStyleFromRulesAndContext(element, document
().documentElement()); |
520 | 528 |
521 newInlineStyle->removeStyleFromRulesAndContext(element, context); | 529 newInlineStyle->removeStyleFromRulesAndContext(element, context); |
522 } | 530 } |
523 | 531 |
524 if (!inlineStyle || newInlineStyle->isEmpty()) { | 532 if (!inlineStyle || newInlineStyle->isEmpty()) { |
525 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT
ag(element, AllowNonEmptyStyleAttribute)) { | 533 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT
ag(element, AllowNonEmptyStyleAttribute)) { |
526 insertedNodes.willRemoveNodePreservingChildren(*element); | 534 insertedNodes.willRemoveNodePreservingChildren(*element); |
527 removeNodePreservingChildren(element); | 535 removeNodePreservingChildren(element); |
528 continue; | 536 continue; |
529 } | 537 } |
530 removeNodeAttribute(element, styleAttr); | 538 removeElementAttribute(element, styleAttr); |
531 } else if (newInlineStyle->style()->propertyCount() != inlineStyle->prop
ertyCount()) { | 539 } else if (newInlineStyle->style()->propertyCount() != inlineStyle->prop
ertyCount()) { |
532 setNodeAttribute(element, styleAttr, AtomicString(newInlineStyle->st
yle()->asText())); | 540 setNodeAttribute(element, styleAttr, AtomicString(newInlineStyle->st
yle()->asText())); |
533 } | 541 } |
534 | 542 |
535 // FIXME: Tolerate differences in id, class, and style attributes. | 543 // FIXME: Tolerate differences in id, class, and style attributes. |
536 if (element->parentNode() && isNonTableCellHTMLBlockElement(element) &&
areIdenticalElements(element, element->parentNode()) | 544 if (element->parentNode() && isNonTableCellHTMLBlockElement(element) &&
areIdenticalElements(element, element->parentNode()) |
537 && VisiblePosition(firstPositionInNode(element->parentNode())) == Vi
siblePosition(firstPositionInNode(element)) | 545 && VisiblePosition(firstPositionInNode(element->parentNode())) == Vi
siblePosition(firstPositionInNode(element)) |
538 && VisiblePosition(lastPositionInNode(element->parentNode())) == Vis
iblePosition(lastPositionInNode(element))) { | 546 && VisiblePosition(lastPositionInNode(element->parentNode())) == Vis
iblePosition(lastPositionInNode(element))) { |
539 insertedNodes.willRemoveNodePreservingChildren(*element); | 547 insertedNodes.willRemoveNodePreservingChildren(*element); |
540 removeNodePreservingChildren(element); | 548 removeNodePreservingChildren(element); |
541 continue; | 549 continue; |
542 } | 550 } |
543 | 551 |
544 if (element->parentNode() && element->parentNode()->rendererIsRichlyEdit
able()) | 552 if (element->parentNode() && element->parentNode()->rendererIsRichlyEdit
able()) |
545 removeNodeAttribute(element, contenteditableAttr); | 553 removeElementAttribute(element, contenteditableAttr); |
546 | 554 |
547 // WebKit used to not add display: inline and float: none on copy. | 555 // WebKit used to not add display: inline and float: none on copy. |
548 // Keep this code around for backward compatibility | 556 // Keep this code around for backward compatibility |
549 if (isLegacyAppleStyleSpan(element)) { | 557 if (isLegacyAppleHTMLSpanElement(element)) { |
550 if (!element->hasChildren()) { | 558 if (!element->hasChildren()) { |
551 insertedNodes.willRemoveNodePreservingChildren(*element); | 559 insertedNodes.willRemoveNodePreservingChildren(*element); |
552 removeNodePreservingChildren(element); | 560 removeNodePreservingChildren(element); |
553 continue; | 561 continue; |
554 } | 562 } |
555 // There are other styles that style rules can give to style spans, | 563 // There are other styles that style rules can give to style spans, |
556 // but these are the two important ones because they'll prevent | 564 // but these are the two important ones because they'll prevent |
557 // inserted content from appearing in the right paragraph. | 565 // inserted content from appearing in the right paragraph. |
558 // FIXME: Hyatt is concerned that selectively using display:inline w
ill give inconsistent | 566 // 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 | 567 // 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 Loading... |
628 void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuild
er(const InsertedNodes& insertedNodes) | 636 void ReplaceSelectionCommand::makeInsertedContentRoundTrippableWithHTMLTreeBuild
er(const InsertedNodes& insertedNodes) |
629 { | 637 { |
630 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf(); | 638 RefPtrWillBeRawPtr<Node> pastEndNode = insertedNodes.pastLastLeaf(); |
631 RefPtrWillBeRawPtr<Node> next = nullptr; | 639 RefPtrWillBeRawPtr<Node> next = nullptr; |
632 for (RefPtrWillBeRawPtr<Node> node = insertedNodes.firstNodeInserted(); node
&& node != pastEndNode; node = next) { | 640 for (RefPtrWillBeRawPtr<Node> node = insertedNodes.firstNodeInserted(); node
&& node != pastEndNode; node = next) { |
633 next = NodeTraversal::next(*node); | 641 next = NodeTraversal::next(*node); |
634 | 642 |
635 if (!node->isHTMLElement()) | 643 if (!node->isHTMLElement()) |
636 continue; | 644 continue; |
637 | 645 |
638 if (isProhibitedParagraphChild(toHTMLElement(node)->localName())) { | 646 HTMLElement& element = toHTMLElement(*node); |
639 if (HTMLElement* paragraphElement = toHTMLElement(enclosingElementWi
thTag(positionInParentBeforeNode(*node), pTag))) | 647 if (isProhibitedParagraphChild(element.localName())) { |
640 moveNodeOutOfAncestor(node, paragraphElement); | 648 if (HTMLElement* paragraphElement = toHTMLElement(enclosingElementWi
thTag(positionInParentBeforeNode(element), pTag))) |
| 649 moveElementOutOfAncestor(&element, paragraphElement); |
641 } | 650 } |
642 | 651 |
643 if (isHeaderElement(node.get())) { | 652 if (isHTMLHeaderElement(&element)) { |
644 if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeO
fType(positionInParentBeforeNode(*node), isHeaderElement))) | 653 if (HTMLElement* headerElement = toHTMLElement(highestEnclosingNodeO
fType(positionInParentBeforeNode(element), isHTMLHeaderElement))) |
645 moveNodeOutOfAncestor(node, headerElement); | 654 moveElementOutOfAncestor(&element, headerElement); |
646 } | 655 } |
647 } | 656 } |
648 } | 657 } |
649 | 658 |
650 void ReplaceSelectionCommand::moveNodeOutOfAncestor(PassRefPtrWillBeRawPtr<Node>
prpNode, PassRefPtrWillBeRawPtr<ContainerNode> prpAncestor) | 659 void ReplaceSelectionCommand::moveElementOutOfAncestor(PassRefPtrWillBeRawPtr<El
ement> prpElement, PassRefPtrWillBeRawPtr<ContainerNode> prpAncestor) |
651 { | 660 { |
652 RefPtrWillBeRawPtr<Node> node = prpNode; | 661 RefPtrWillBeRawPtr<Element> element = prpElement; |
653 RefPtrWillBeRawPtr<Node> ancestor = prpAncestor; | 662 RefPtrWillBeRawPtr<ContainerNode> ancestor = prpAncestor; |
654 | 663 |
655 if (!ancestor->parentNode()->hasEditableStyle()) | 664 if (!ancestor->parentNode()->hasEditableStyle()) |
656 return; | 665 return; |
657 | 666 |
658 VisiblePosition positionAtEndOfNode(lastPositionInOrAfterNode(node.get())); | 667 VisiblePosition positionAtEndOfNode(lastPositionInOrAfterNode(element.get())
); |
659 VisiblePosition lastPositionInParagraph(lastPositionInNode(ancestor.get())); | 668 VisiblePosition lastPositionInParagraph(lastPositionInNode(ancestor.get())); |
660 if (positionAtEndOfNode == lastPositionInParagraph) { | 669 if (positionAtEndOfNode == lastPositionInParagraph) { |
661 removeNode(node); | 670 removeNode(element); |
662 if (ancestor->nextSibling()) | 671 if (ancestor->nextSibling()) |
663 insertNodeBefore(node, ancestor->nextSibling()); | 672 insertNodeBefore(element, ancestor->nextSibling()); |
664 else | 673 else |
665 appendNode(node, ancestor->parentNode()); | 674 appendNode(element, ancestor->parentNode()); |
666 } else { | 675 } else { |
667 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(node.get(), anc
estor.get(), true); | 676 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(element.get(),
ancestor.get(), true); |
668 removeNode(node); | 677 removeNode(element); |
669 insertNodeBefore(node, nodeToSplitTo); | 678 insertNodeBefore(element, nodeToSplitTo); |
670 } | 679 } |
671 if (!ancestor->hasChildren()) | 680 if (!ancestor->hasChildren()) |
672 removeNode(ancestor.release()); | 681 removeNode(ancestor.release()); |
673 } | 682 } |
674 | 683 |
675 static inline bool nodeHasVisibleRenderText(Text& text) | 684 static inline bool nodeHasVisibleRenderText(Text& text) |
676 { | 685 { |
677 return text.renderer() && text.renderer()->renderedTextLength() > 0; | 686 return text.renderer() && text.renderer()->renderedTextLength() > 0; |
678 } | 687 } |
679 | 688 |
(...skipping 14 matching lines...) Expand all Loading... |
694 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); | 703 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); |
695 if (firstNodeInserted && firstNodeInserted->isTextNode() && !nodeHasVisibleR
enderText(toText(*firstNodeInserted))) { | 704 if (firstNodeInserted && firstNodeInserted->isTextNode() && !nodeHasVisibleR
enderText(toText(*firstNodeInserted))) { |
696 insertedNodes.willRemoveNode(*firstNodeInserted); | 705 insertedNodes.willRemoveNode(*firstNodeInserted); |
697 removeNode(firstNodeInserted); | 706 removeNode(firstNodeInserted); |
698 } | 707 } |
699 } | 708 } |
700 | 709 |
701 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const | 710 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const |
702 { | 711 { |
703 // FIXME: Why is this hack here? What's special about <select> tags? | 712 // FIXME: Why is this hack here? What's special about <select> tags? |
704 Element* enclosingSelect = enclosingElementWithTag(m_endOfInsertedContent, s
electTag); | 713 HTMLSelectElement* enclosingSelect = toHTMLSelectElement(enclosingElementWit
hTag(m_endOfInsertedContent, selectTag)); |
705 return VisiblePosition(enclosingSelect ? lastPositionInOrAfterNode(enclosing
Select) : m_endOfInsertedContent); | 714 return VisiblePosition(enclosingSelect ? lastPositionInOrAfterNode(enclosing
Select) : m_endOfInsertedContent); |
706 } | 715 } |
707 | 716 |
708 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons
t | 717 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons
t |
709 { | 718 { |
710 return VisiblePosition(m_startOfInsertedContent); | 719 return VisiblePosition(m_startOfInsertedContent); |
711 } | 720 } |
712 | 721 |
713 static void removeHeadContents(ReplacementFragment& fragment) | 722 static void removeHeadContents(ReplacementFragment& fragment) |
714 { | 723 { |
(...skipping 13 matching lines...) Expand all Loading... |
728 } | 737 } |
729 | 738 |
730 // Remove style spans before insertion if they are unnecessary. It's faster bec
ause we'll | 739 // Remove style spans before insertion if they are unnecessary. It's faster bec
ause we'll |
731 // avoid doing a layout. | 740 // avoid doing a layout. |
732 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const
Position& insertionPos) | 741 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const
Position& insertionPos) |
733 { | 742 { |
734 Node* topNode = fragment.firstChild(); | 743 Node* topNode = fragment.firstChild(); |
735 | 744 |
736 // Handling the case where we are doing Paste as Quotation or pasting into q
uoted content is more complicated (see handleStyleSpans) | 745 // 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. | 746 // and doesn't receive the optimization. |
738 if (isMailPasteAsQuotationNode(topNode) || enclosingNodeOfType(firstPosition
InOrBeforeNode(topNode), isMailHTMLBlockquoteElement, CanCrossEditingBoundary)) | 747 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || enclosingNodeOfT
ype(firstPositionInOrBeforeNode(topNode), isMailHTMLBlockquoteElement, CanCrossE
ditingBoundary)) |
739 return false; | 748 return false; |
740 | 749 |
741 // Either there are no style spans in the fragment or a WebKit client has ad
ded content to the fragment | 750 // 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. | 751 // before inserting it. Look for and handle style spans after insertion. |
743 if (!isLegacyAppleStyleSpan(topNode)) | 752 if (!isLegacyAppleHTMLSpanElement(topNode)) |
744 return false; | 753 return false; |
745 | 754 |
746 Node* wrappingStyleSpan = topNode; | 755 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode); |
747 RefPtrWillBeRawPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(
insertionPos.parentAnchoredEquivalent()); | 756 RefPtrWillBeRawPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(
insertionPos.parentAnchoredEquivalent()); |
748 String styleText = styleAtInsertionPos->style()->asText(); | 757 String styleText = styleAtInsertionPos->style()->asText(); |
749 | 758 |
750 // FIXME: This string comparison is a naive way of comparing two styles. | 759 // 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. | 760 // We should be taking the diff and check that the diff is empty. |
752 if (styleText != toElement(wrappingStyleSpan)->getAttribute(styleAttr)) | 761 if (styleText != wrappingStyleSpan->getAttribute(styleAttr)) |
753 return false; | 762 return false; |
754 | 763 |
755 fragment.removeNodePreservingChildren(wrappingStyleSpan); | 764 fragment.removeNodePreservingChildren(wrappingStyleSpan); |
756 return true; | 765 return true; |
757 } | 766 } |
758 | 767 |
759 // At copy time, WebKit wraps copied content in a span that contains the source
document's | 768 // 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 | 769 // default styles. If the copied Range inherits any other styles from its ances
tors, we put |
761 // those styles on a second span. | 770 // those styles on a second span. |
762 // This function removes redundant styles from those spans, and removes the span
s if all their | 771 // This function removes redundant styles from those spans, and removes the span
s if all their |
763 // styles are redundant. | 772 // styles are redundant. |
764 // We should remove the Apple-style-span class when we're done, see <rdar://prob
lem/5685600>. | 773 // 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 | 774 // We should remove styles from spans that are overridden by all of their childr
en, either here |
766 // or at copy time. | 775 // or at copy time. |
767 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes) | 776 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes) |
768 { | 777 { |
769 HTMLElement* wrappingStyleSpan = 0; | 778 HTMLSpanElement* wrappingStyleSpan = 0; |
770 // The style span that contains the source document's default style should b
e at | 779 // 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), | 780 // 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. | 781 // 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)) { | 782 for (Node* node = insertedNodes.firstNodeInserted(); node; node = NodeTraver
sal::next(*node)) { |
774 if (isLegacyAppleStyleSpan(node)) { | 783 if (isLegacyAppleHTMLSpanElement(node)) { |
775 wrappingStyleSpan = toHTMLElement(node); | 784 wrappingStyleSpan = toHTMLSpanElement(node); |
776 break; | 785 break; |
777 } | 786 } |
778 } | 787 } |
779 | 788 |
780 // There might not be any style spans if we're pasting from another applicat
ion or if | 789 // 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. | 790 // we are here because of a document.execCommand("InsertHTML", ...) call. |
782 if (!wrappingStyleSpan) | 791 if (!wrappingStyleSpan) |
783 return; | 792 return; |
784 | 793 |
785 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(wrappingStyleS
pan->inlineStyle()); | 794 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(wrappingStyleS
pan->inlineStyle()); |
786 ContainerNode* context = wrappingStyleSpan->parentNode(); | 795 ContainerNode* context = wrappingStyleSpan->parentNode(); |
787 | 796 |
788 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if yo
u're pasting into a quoted region, | 797 // 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>. | 798 // 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); | 799 HTMLQuoteElement* blockquoteElement = isMailPasteAsQuotationHTMLBlockQuoteEl
ement(context) ? |
791 if (blockquoteNode) | 800 toHTMLQuoteElement(context) : |
| 801 toHTMLQuoteElement(enclosingNodeOfType(firstPositionInNode(context), isM
ailHTMLBlockquoteElement, CanCrossEditingBoundary)); |
| 802 if (blockquoteElement) |
792 context = document().documentElement(); | 803 context = document().documentElement(); |
793 | 804 |
794 // This operation requires that only editing styles to be removed from sourc
eDocumentStyle. | 805 // This operation requires that only editing styles to be removed from sourc
eDocumentStyle. |
795 style->prepareToApplyAt(firstPositionInNode(context)); | 806 style->prepareToApplyAt(firstPositionInNode(context)); |
796 | 807 |
797 // Remove block properties in the span's style. This prevents properties tha
t probably have no effect | 808 // 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 | 809 // currently from affecting blocks later if the style is cloned for a new bl
ock element during a future |
799 // editing operation. | 810 // editing operation. |
800 // FIXME: They *can* have an effect currently if blocks beneath the style sp
an aren't individually marked | 811 // 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. | 812 // 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 Loading... |
861 // Stop if any previous sibling is a block. | 872 // Stop if any previous sibling is a block. |
862 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling
->previousSibling()) { | 873 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling
->previousSibling()) { |
863 if (isBlockFlowElement(*sibling)) | 874 if (isBlockFlowElement(*sibling)) |
864 return node; | 875 return node; |
865 } | 876 } |
866 node = parent; | 877 node = parent; |
867 } | 878 } |
868 return node; | 879 return node; |
869 } | 880 } |
870 | 881 |
871 static bool isInlineNodeWithStyle(const Node* node) | 882 static bool isInlineHTMLElementWithStyle(const Node* node) |
872 { | 883 { |
873 // We don't want to skip over any block elements. | 884 // We don't want to skip over any block elements. |
874 if (isBlock(node)) | 885 if (isBlock(node)) |
875 return false; | 886 return false; |
876 | 887 |
877 if (!node->isHTMLElement()) | 888 if (!node->isHTMLElement()) |
878 return false; | 889 return false; |
879 | 890 |
880 // We can skip over elements whose class attribute is | 891 // We can skip over elements whose class attribute is |
881 // one of our internal classes. | 892 // one of our internal classes. |
882 const HTMLElement* element = toHTMLElement(node); | 893 const HTMLElement* element = toHTMLElement(node); |
883 const AtomicString& classAttributeValue = element->getAttribute(classAttr); | 894 const AtomicString& classAttributeValue = element->getAttribute(classAttr); |
884 if (classAttributeValue == AppleTabSpanClass) { | 895 if (classAttributeValue == AppleTabSpanClass) { |
885 UseCounter::count(node->document(), UseCounter::EditingAppleTabSpanClass
); | 896 UseCounter::count(element->document(), UseCounter::EditingAppleTabSpanCl
ass); |
886 return true; | 897 return true; |
887 } | 898 } |
888 if (classAttributeValue == AppleConvertedSpace) { | 899 if (classAttributeValue == AppleConvertedSpace) { |
889 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpa
ce); | 900 UseCounter::count(element->document(), UseCounter::EditingAppleConverted
Space); |
890 return true; | 901 return true; |
891 } | 902 } |
892 if (classAttributeValue == ApplePasteAsQuotation) { | 903 if (classAttributeValue == ApplePasteAsQuotation) { |
893 UseCounter::count(node->document(), UseCounter::EditingApplePasteAsQuota
tion); | 904 UseCounter::count(element->document(), UseCounter::EditingApplePasteAsQu
otation); |
894 return true; | 905 return true; |
895 } | 906 } |
896 | 907 |
897 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element); | 908 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element); |
898 } | 909 } |
899 | 910 |
900 inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& i
nsertionPos) | 911 static inline HTMLElement* elementToSplitToAvoidPastingIntoInlineElementsWithSty
le(const Position& insertionPos) |
901 { | 912 { |
902 Element* containingBlock = enclosingBlock(insertionPos.containerNode()); | 913 Element* containingBlock = enclosingBlock(insertionPos.containerNode()); |
903 return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, Canno
tCrossEditingBoundary, containingBlock); | 914 return toHTMLElement(highestEnclosingNodeOfType(insertionPos, isInlineHTMLEl
ementWithStyle, CannotCrossEditingBoundary, containingBlock)); |
904 } | 915 } |
905 | 916 |
906 void ReplaceSelectionCommand::doApply() | 917 void ReplaceSelectionCommand::doApply() |
907 { | 918 { |
908 VisibleSelection selection = endingSelection(); | 919 VisibleSelection selection = endingSelection(); |
909 ASSERT(selection.isCaretOrRange()); | 920 ASSERT(selection.isCaretOrRange()); |
910 ASSERT(selection.start().deprecatedNode()); | 921 ASSERT(selection.start().deprecatedNode()); |
911 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedN
ode()) | 922 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedN
ode()) |
912 return; | 923 return; |
913 | 924 |
(...skipping 13 matching lines...) Expand all Loading... |
927 m_insertionStyle = EditingStyle::create(selection.start()); | 938 m_insertionStyle = EditingStyle::create(selection.start()); |
928 m_insertionStyle->mergeTypingStyle(&document()); | 939 m_insertionStyle->mergeTypingStyle(&document()); |
929 } | 940 } |
930 | 941 |
931 VisiblePosition visibleStart = selection.visibleStart(); | 942 VisiblePosition visibleStart = selection.visibleStart(); |
932 VisiblePosition visibleEnd = selection.visibleEnd(); | 943 VisiblePosition visibleEnd = selection.visibleEnd(); |
933 | 944 |
934 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); | 945 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); |
935 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); | 946 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); |
936 | 947 |
937 Node* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEquival
ent().deprecatedNode()); | 948 Element* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEqui
valent().deprecatedNode()); |
938 | 949 |
939 Position insertionPos = selection.start(); | 950 Position insertionPos = selection.start(); |
940 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailH
TMLBlockquoteElement, CanCrossEditingBoundary); | 951 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailH
TMLBlockquoteElement, CanCrossEditingBoundary); |
941 bool selectionIsPlainText = !selection.isContentRichlyEditable(); | 952 bool selectionIsPlainText = !selection.isContentRichlyEditable(); |
942 Element* currentRoot = selection.rootEditableElement(); | 953 Element* currentRoot = selection.rootEditableElement(); |
943 | 954 |
944 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !
startIsInsideMailBlockquote) || | 955 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !
startIsInsideMailBlockquote) || |
945 enclosingBlockOfVisibleStart == currentRoot || isListItem(enclosingBlock
OfVisibleStart) || selectionIsPlainText) | 956 enclosingBlockOfVisibleStart == currentRoot || isListItem(enclosingBlock
OfVisibleStart) || selectionIsPlainText) |
946 m_preventNesting = false; | 957 m_preventNesting = false; |
947 | 958 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1004 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. | 1015 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. |
1005 prepareWhitespaceAtPositionForSplit(insertionPos); | 1016 prepareWhitespaceAtPositionForSplit(insertionPos); |
1006 | 1017 |
1007 // If the downstream node has been removed there's no point in continuing. | 1018 // If the downstream node has been removed there's no point in continuing. |
1008 if (!insertionPos.downstream().deprecatedNode()) | 1019 if (!insertionPos.downstream().deprecatedNode()) |
1009 return; | 1020 return; |
1010 | 1021 |
1011 // NOTE: This would be an incorrect usage of downstream() if downstream() we
re changed to mean the last position after | 1022 // 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 | 1023 // 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]). | 1024 // 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; | 1025 HTMLBRElement* endBR = isHTMLBRElement(*insertionPos.downstream().deprecated
Node()) ? toHTMLBRElement(insertionPos.downstream().deprecatedNode()) : 0; |
1015 VisiblePosition originalVisPosBeforeEndBR; | 1026 VisiblePosition originalVisPosBeforeEndBR; |
1016 if (endBR) | 1027 if (endBR) |
1017 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D
OWNSTREAM).previous(); | 1028 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D
OWNSTREAM).previous(); |
1018 | 1029 |
1019 RefPtrWillBeRawPtr<Node> enclosingBlockOfInsertionPos = enclosingBlock(inser
tionPos.deprecatedNode()); | 1030 RefPtrWillBeRawPtr<Element> enclosingBlockOfInsertionPos = enclosingBlock(in
sertionPos.deprecatedNode()); |
1020 | 1031 |
1021 // Adjust insertionPos to prevent nesting. | 1032 // Adjust insertionPos to prevent nesting. |
1022 // If the start was in a Mail blockquote, we will have already handled adjus
ting insertionPos above. | 1033 // 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) { | 1034 if (m_preventNesting && enclosingBlockOfInsertionPos && !isTableCell(enclosi
ngBlockOfInsertionPos.get()) && !startIsInsideMailBlockquote) { |
1024 ASSERT(enclosingBlockOfInsertionPos != currentRoot); | 1035 ASSERT(enclosingBlockOfInsertionPos != currentRoot); |
1025 VisiblePosition visibleInsertionPos(insertionPos); | 1036 VisiblePosition visibleInsertionPos(insertionPos); |
1026 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti
onPos) && fragment.hasInterchangeNewlineAtEnd())) | 1037 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti
onPos) && fragment.hasInterchangeNewlineAtEnd())) |
1027 insertionPos = positionInParentAfterNode(*enclosingBlockOfInsertionP
os); | 1038 insertionPos = positionInParentAfterNode(*enclosingBlockOfInsertionP
os); |
1028 else if (isStartOfBlock(visibleInsertionPos)) | 1039 else if (isStartOfBlock(visibleInsertionPos)) |
1029 insertionPos = positionInParentBeforeNode(*enclosingBlockOfInsertion
Pos); | 1040 insertionPos = positionInParentBeforeNode(*enclosingBlockOfInsertion
Pos); |
(...skipping 28 matching lines...) Expand all Loading... |
1058 // This way we can produce a less verbose markup. | 1069 // This way we can produce a less verbose markup. |
1059 // We can skip this optimization for fragments not wrapped in one of | 1070 // We can skip this optimization for fragments not wrapped in one of |
1060 // our style spans and for positions inside list items | 1071 // our style spans and for positions inside list items |
1061 // since insertAsListItems already does the right thing. | 1072 // since insertAsListItems already does the right thing. |
1062 if (!m_matchStyle && !enclosingList(insertionPos.containerNode())) { | 1073 if (!m_matchStyle && !enclosingList(insertionPos.containerNode())) { |
1063 if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInC
ontainerNode() && !insertionPos.atLastEditingPositionForNode()) { | 1074 if (insertionPos.containerNode()->isTextNode() && insertionPos.offsetInC
ontainerNode() && !insertionPos.atLastEditingPositionForNode()) { |
1064 splitTextNode(insertionPos.containerText(), insertionPos.offsetInCon
tainerNode()); | 1075 splitTextNode(insertionPos.containerText(), insertionPos.offsetInCon
tainerNode()); |
1065 insertionPos = firstPositionInNode(insertionPos.containerNode()); | 1076 insertionPos = firstPositionInNode(insertionPos.containerNode()); |
1066 } | 1077 } |
1067 | 1078 |
1068 if (RefPtrWillBeRawPtr<Node> nodeToSplitTo = nodeToSplitToAvoidPastingIn
toInlineNodesWithStyle(insertionPos)) { | 1079 if (RefPtrWillBeRawPtr<HTMLElement> elementToSplitTo = elementToSplitToA
voidPastingIntoInlineElementsWithStyle(insertionPos)) { |
1069 if (insertionPos.containerNode() != nodeToSplitTo->parentNode()) { | 1080 if (insertionPos.containerNode() != elementToSplitTo->parentNode())
{ |
1070 Node* splitStart = insertionPos.computeNodeAfterPosition(); | 1081 Node* splitStart = insertionPos.computeNodeAfterPosition(); |
1071 if (!splitStart) | 1082 if (!splitStart) |
1072 splitStart = insertionPos.containerNode(); | 1083 splitStart = insertionPos.containerNode(); |
1073 nodeToSplitTo = splitTreeToNode(splitStart, nodeToSplitTo->paren
tNode()).get(); | 1084 RefPtrWillBeRawPtr<Node> nodeToSplitTo = splitTreeToNode(splitSt
art, elementToSplitTo->parentNode()).get(); |
1074 insertionPos = positionInParentBeforeNode(*nodeToSplitTo); | 1085 insertionPos = positionInParentBeforeNode(*nodeToSplitTo); |
1075 } | 1086 } |
1076 } | 1087 } |
1077 } | 1088 } |
1078 | 1089 |
1079 // FIXME: When pasting rich content we're often prevented from heading down
the fast path by style spans. Try | 1090 // 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. | 1091 // again here if they've been removed. |
1081 | 1092 |
1082 // 1) Insert the content. | 1093 // 1) Insert the content. |
1083 // 2) Remove redundant styles and style tags, this inner <b> for example: <b
>foo <b>bar</b> baz</b>. | 1094 // 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. | 1095 // 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, | 1096 // 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 | 1097 // 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 | 1098 // end of the selection that was pasted into, or c) handle an interchange ne
wline at the end of the |
1088 // incoming fragment. | 1099 // incoming fragment. |
1089 // 5) Add spaces for smart replace. | 1100 // 5) Add spaces for smart replace. |
1090 // 6) Select the replacement if requested, and match style if requested. | 1101 // 6) Select the replacement if requested, and match style if requested. |
1091 | 1102 |
1092 InsertedNodes insertedNodes; | 1103 InsertedNodes insertedNodes; |
1093 RefPtrWillBeRawPtr<Node> refNode = fragment.firstChild(); | 1104 RefPtrWillBeRawPtr<Node> refNode = fragment.firstChild(); |
1094 ASSERT(refNode); | 1105 ASSERT(refNode); |
1095 RefPtrWillBeRawPtr<Node> node = refNode->nextSibling(); | 1106 RefPtrWillBeRawPtr<Node> node = refNode->nextSibling(); |
1096 | 1107 |
1097 fragment.removeNode(refNode); | 1108 fragment.removeNode(refNode); |
1098 | 1109 |
1099 Node* blockStart = enclosingBlock(insertionPos.deprecatedNode()); | 1110 Element* blockStart = enclosingBlock(insertionPos.deprecatedNode()); |
1100 if ((isHTMLListElement(refNode.get()) || (isLegacyAppleStyleSpan(refNode.get
()) && isHTMLListElement(refNode->firstChild()))) | 1111 if ((isHTMLListElement(refNode.get()) || (isLegacyAppleHTMLSpanElement(refNo
de.get()) && isHTMLListElement(refNode->firstChild()))) |
1101 && blockStart && blockStart->renderer()->isListItem()) | 1112 && blockStart && blockStart->renderer()->isListItem()) |
1102 refNode = insertAsListItems(toHTMLElement(refNode), blockStart, insertio
nPos, insertedNodes); | 1113 refNode = insertAsListItems(toHTMLElement(refNode), blockStart, insertio
nPos, insertedNodes); |
1103 else { | 1114 else { |
1104 insertNodeAt(refNode, insertionPos); | 1115 insertNodeAt(refNode, insertionPos); |
1105 insertedNodes.respondToNodeInsertion(*refNode); | 1116 insertedNodes.respondToNodeInsertion(*refNode); |
1106 } | 1117 } |
1107 | 1118 |
1108 // Mutation events (bug 22634) may have already removed the inserted content | 1119 // Mutation events (bug 22634) may have already removed the inserted content |
1109 if (!refNode->inDocument()) | 1120 if (!refNode->inDocument()) |
1110 return; | 1121 return; |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1207 } | 1218 } |
1208 | 1219 |
1209 Position lastPositionToSelect; | 1220 Position lastPositionToSelect; |
1210 if (fragment.hasInterchangeNewlineAtEnd()) { | 1221 if (fragment.hasInterchangeNewlineAtEnd()) { |
1211 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); | 1222 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); |
1212 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound
ary); | 1223 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound
ary); |
1213 | 1224 |
1214 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont
ent) || next.isNull()) { | 1225 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont
ent) || next.isNull()) { |
1215 if (!isStartOfParagraph(endOfInsertedContent)) { | 1226 if (!isStartOfParagraph(endOfInsertedContent)) { |
1216 setEndingSelection(endOfInsertedContent); | 1227 setEndingSelection(endOfInsertedContent); |
1217 Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEq
uivalent().deprecatedNode()); | 1228 Element* enclosingBlockElement = enclosingBlock(endOfInsertedCon
tent.deepEquivalent().deprecatedNode()); |
1218 if (isListItem(enclosingNode)) { | 1229 if (isListItem(enclosingBlockElement)) { |
1219 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt
emElement(document()); | 1230 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt
emElement(document()); |
1220 insertNodeAfter(newListItem, enclosingNode); | 1231 insertNodeAfter(newListItem, enclosingBlockElement); |
1221 setEndingSelection(VisiblePosition(firstPositionInNode(newLi
stItem.get()))); | 1232 setEndingSelection(VisiblePosition(firstPositionInNode(newLi
stItem.get()))); |
1222 } else { | 1233 } else { |
1223 // Use a default paragraph element (a plain div) for the emp
ty paragraph, using the last paragraph | 1234 // 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. | 1235 // block's style seems to annoy users. |
1225 insertParagraphSeparator(true, !startIsInsideMailBlockquote
&& highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), | 1236 insertParagraphSeparator(true, !startIsInsideMailBlockquote
&& highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), |
1226 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary,
insertedNodes.firstNodeInserted()->parentNode())); | 1237 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary,
insertedNodes.firstNodeInserted()->parentNode())); |
1227 } | 1238 } |
1228 | 1239 |
1229 // Select up to the paragraph separator that was added. | 1240 // Select up to the paragraph separator that was added. |
1230 lastPositionToSelect = endingSelection().visibleStart().deepEqui
valent(); | 1241 lastPositionToSelect = endingSelection().visibleStart().deepEqui
valent(); |
1231 updateNodesInserted(lastPositionToSelect.deprecatedNode()); | 1242 updateNodesInserted(lastPositionToSelect.deprecatedNode()); |
1232 } | 1243 } |
1233 } else { | 1244 } else { |
1234 // Select up to the beginning of the next paragraph. | 1245 // Select up to the beginning of the next paragraph. |
1235 lastPositionToSelect = next.deepEquivalent().downstream(); | 1246 lastPositionToSelect = next.deepEquivalent().downstream(); |
1236 } | 1247 } |
| 1248 } else { |
| 1249 mergeEndIfNeeded(); |
| 1250 } |
1237 | 1251 |
1238 } else | 1252 if (HTMLQuoteElement* mailBlockquote = toHTMLQuoteElement(enclosingNodeOfTyp
e(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationHTM
LBlockQuoteElement))) |
1239 mergeEndIfNeeded(); | 1253 removeElementAttribute(mailBlockquote, classAttr); |
1240 | |
1241 if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedCont
ent().deepEquivalent(), isMailPasteAsQuotationNode)) | |
1242 removeNodeAttribute(toElement(mailBlockquote), classAttr); | |
1243 | 1254 |
1244 if (shouldPerformSmartReplace()) | 1255 if (shouldPerformSmartReplace()) |
1245 addSpacesForSmartReplace(); | 1256 addSpacesForSmartReplace(); |
1246 | 1257 |
1247 // If we are dealing with a fragment created from plain text | 1258 // If we are dealing with a fragment created from plain text |
1248 // no style matching is necessary. | 1259 // no style matching is necessary. |
1249 if (plainTextFragment) | 1260 if (plainTextFragment) |
1250 m_matchStyle = false; | 1261 m_matchStyle = false; |
1251 | 1262 |
1252 completeHTMLReplacement(lastPositionToSelect); | 1263 completeHTMLReplacement(lastPositionToSelect); |
1253 } | 1264 } |
1254 | 1265 |
1255 bool ReplaceSelectionCommand::shouldRemoveEndBR(Node* endBR, const VisiblePositi
on& originalVisPosBeforeEndBR) | 1266 bool ReplaceSelectionCommand::shouldRemoveEndBR(HTMLBRElement* endBR, const Visi
blePosition& originalVisPosBeforeEndBR) |
1256 { | 1267 { |
1257 if (!endBR || !endBR->inDocument()) | 1268 if (!endBR || !endBR->inDocument()) |
1258 return false; | 1269 return false; |
1259 | 1270 |
1260 VisiblePosition visiblePos(positionBeforeNode(endBR)); | 1271 VisiblePosition visiblePos(positionBeforeNode(endBR)); |
1261 | 1272 |
1262 // Don't remove the br if nothing was inserted. | 1273 // Don't remove the br if nothing was inserted. |
1263 if (visiblePos.previous() == originalVisPosBeforeEndBR) | 1274 if (visiblePos.previous() == originalVisPosBeforeEndBR) |
1264 return false; | 1275 return false; |
1265 | 1276 |
1266 // Remove the br if it is collapsed away and so is unnecessary. | 1277 // Remove the br if it is collapsed away and so is unnecessary. |
1267 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && !isStartOfPa
ragraph(visiblePos)) | 1278 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && !isStartOfPa
ragraph(visiblePos)) |
1268 return true; | 1279 return true; |
1269 | 1280 |
1270 // A br that was originally holding a line open should be displaced by inser
ted content or turned into a line break. | 1281 // 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. | 1282 // 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); | 1283 return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos); |
1273 } | 1284 } |
1274 | 1285 |
1275 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const | 1286 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const |
1276 { | 1287 { |
1277 if (!m_smartReplace) | 1288 if (!m_smartReplace) |
1278 return false; | 1289 return false; |
1279 | 1290 |
1280 Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedCon
tent().deepEquivalent()); | 1291 HTMLTextFormControlElement* textControl = enclosingTextFormControl(positionA
tStartOfInsertedContent().deepEquivalent()); |
1281 if (isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPa
sswordField()) | 1292 if (isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPa
sswordField()) |
1282 return false; // Disable smart replace for password fields. | 1293 return false; // Disable smart replace for password fields. |
1283 | 1294 |
1284 return true; | 1295 return true; |
1285 } | 1296 } |
1286 | 1297 |
1287 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) | 1298 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) |
1288 { | 1299 { |
1289 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara
cter, previousCharacter); | 1300 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara
cter, previousCharacter); |
1290 } | 1301 } |
(...skipping 12 matching lines...) Expand all Loading... |
1303 } | 1314 } |
1304 | 1315 |
1305 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar
acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte
rAfter(), false); | 1316 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar
acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte
rAfter(), false); |
1306 if (needsTrailingSpace && endNode) { | 1317 if (needsTrailingSpace && endNode) { |
1307 bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->s
tyle()->collapseWhiteSpace(); | 1318 bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->s
tyle()->collapseWhiteSpace(); |
1308 if (endNode->isTextNode()) { | 1319 if (endNode->isTextNode()) { |
1309 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ?
nonBreakingSpaceString() : " "); | 1320 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ?
nonBreakingSpaceString() : " "); |
1310 if (m_endOfInsertedContent.containerNode() == endNode) | 1321 if (m_endOfInsertedContent.containerNode() == endNode) |
1311 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse
tInContainerNode() + 1); | 1322 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse
tInContainerNode() + 1); |
1312 } else { | 1323 } else { |
1313 RefPtrWillBeRawPtr<Node> node = document().createEditingTextNode(col
lapseWhiteSpace ? nonBreakingSpaceString() : " "); | 1324 RefPtrWillBeRawPtr<Text> node = document().createEditingTextNode(col
lapseWhiteSpace ? nonBreakingSpaceString() : " "); |
1314 insertNodeAfter(node, endNode); | 1325 insertNodeAfter(node, endNode); |
1315 updateNodesInserted(node.get()); | 1326 updateNodesInserted(node.get()); |
1316 } | 1327 } |
1317 } | 1328 } |
1318 | 1329 |
1319 document().updateLayout(); | 1330 document().updateLayout(); |
1320 | 1331 |
1321 Position startDownstream = startOfInsertedContent.deepEquivalent().downstrea
m(); | 1332 Position startDownstream = startOfInsertedContent.deepEquivalent().downstrea
m(); |
1322 Node* startNode = startDownstream.computeNodeAfterPosition(); | 1333 Node* startNode = startDownstream.computeNodeAfterPosition(); |
1323 unsigned startOffset = 0; | 1334 unsigned startOffset = 0; |
1324 if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) { | 1335 if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) { |
1325 startNode = startDownstream.containerNode(); | 1336 startNode = startDownstream.containerNode(); |
1326 startOffset = startDownstream.offsetInContainerNode(); | 1337 startOffset = startDownstream.offsetInContainerNode(); |
1327 } | 1338 } |
1328 | 1339 |
1329 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC
haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre
vious().characterAfter(), true); | 1340 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC
haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre
vious().characterAfter(), true); |
1330 if (needsLeadingSpace && startNode) { | 1341 if (needsLeadingSpace && startNode) { |
1331 bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer(
)->style()->collapseWhiteSpace(); | 1342 bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer(
)->style()->collapseWhiteSpace(); |
1332 if (startNode->isTextNode()) { | 1343 if (startNode->isTextNode()) { |
1333 insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpac
e ? nonBreakingSpaceString() : " "); | 1344 insertTextIntoNode(toText(startNode), startOffset, collapseWhiteSpac
e ? nonBreakingSpaceString() : " "); |
1334 if (m_endOfInsertedContent.containerNode() == startNode && m_endOfIn
sertedContent.offsetInContainerNode()) | 1345 if (m_endOfInsertedContent.containerNode() == startNode && m_endOfIn
sertedContent.offsetInContainerNode()) |
1335 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse
tInContainerNode() + 1); | 1346 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offse
tInContainerNode() + 1); |
1336 } else { | 1347 } else { |
1337 RefPtrWillBeRawPtr<Node> node = document().createEditingTextNode(col
lapseWhiteSpace ? nonBreakingSpaceString() : " "); | 1348 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, | 1349 // 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. | 1350 // but m_endOfInsertedContent is supposed to mark the end of pasted
content. |
1340 insertNodeBefore(node, startNode); | 1351 insertNodeBefore(node, startNode); |
1341 m_startOfInsertedContent = firstPositionInNode(node.get()); | 1352 m_startOfInsertedContent = firstPositionInNode(node.get()); |
1342 } | 1353 } |
1343 } | 1354 } |
1344 } | 1355 } |
1345 | 1356 |
1346 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
onToSelect) | 1357 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositi
onToSelect) |
1347 { | 1358 { |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1431 } | 1442 } |
1432 } | 1443 } |
1433 | 1444 |
1434 EditAction ReplaceSelectionCommand::editingAction() const | 1445 EditAction ReplaceSelectionCommand::editingAction() const |
1435 { | 1446 { |
1436 return m_editAction; | 1447 return m_editAction; |
1437 } | 1448 } |
1438 | 1449 |
1439 // If the user is inserting a list into an existing list, instead of nesting the
list, | 1450 // 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. | 1451 // 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) | 1452 Node* ReplaceSelectionCommand::insertAsListItems(PassRefPtrWillBeRawPtr<HTMLElem
ent> prpListElement, Element* insertionBlock, const Position& insertPos, Inserte
dNodes& insertedNodes) |
1442 { | 1453 { |
1443 RefPtrWillBeRawPtr<HTMLElement> listElement = prpListElement; | 1454 RefPtrWillBeRawPtr<HTMLElement> listElement = prpListElement; |
1444 | 1455 |
1445 while (listElement->hasOneChild() && isHTMLListElement(listElement->firstChi
ld())) | 1456 while (listElement->hasOneChild() && isHTMLListElement(listElement->firstChi
ld())) |
1446 listElement = toHTMLElement(listElement->firstChild()); | 1457 listElement = toHTMLElement(listElement->firstChild()); |
1447 | 1458 |
1448 bool isStart = isStartOfParagraph(VisiblePosition(insertPos)); | 1459 bool isStart = isStartOfParagraph(VisiblePosition(insertPos)); |
1449 bool isEnd = isEndOfParagraph(VisiblePosition(insertPos)); | 1460 bool isEnd = isEndOfParagraph(VisiblePosition(insertPos)); |
1450 bool isMiddle = !isStart && !isEnd; | 1461 bool isMiddle = !isStart && !isEnd; |
1451 Node* lastNode = insertionBlock; | 1462 Node* lastNode = insertionBlock; |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1496 bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f
ragment) | 1507 bool ReplaceSelectionCommand::performTrivialReplace(const ReplacementFragment& f
ragment) |
1497 { | 1508 { |
1498 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild()
|| !fragment.firstChild()->isTextNode()) | 1509 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild()
|| !fragment.firstChild()->isTextNode()) |
1499 return false; | 1510 return false; |
1500 | 1511 |
1501 // FIXME: Would be nice to handle smart replace in the fast path. | 1512 // FIXME: Would be nice to handle smart replace in the fast path. |
1502 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha
sInterchangeNewlineAtEnd()) | 1513 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha
sInterchangeNewlineAtEnd()) |
1503 return false; | 1514 return false; |
1504 | 1515 |
1505 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" s
hould not be underlined. | 1516 // 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())) | 1517 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle(endingSelection(
).start())) |
1507 return false; | 1518 return false; |
1508 | 1519 |
1509 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = endingSelection().end().dow
nstream().anchorNode(); | 1520 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = endingSelection().end().dow
nstream().anchorNode(); |
1510 Text* textNode = toText(fragment.firstChild()); | 1521 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. | 1522 // Our fragment creation code handles tabs, spaces, and newlines, so we don'
t have to worry about those here. |
1512 | 1523 |
1513 Position start = endingSelection().start(); | 1524 Position start = endingSelection().start(); |
1514 Position end = replaceSelectedTextInNode(textNode->data()); | 1525 Position end = replaceSelectedTextInNode(textNode->data()); |
1515 if (end.isNull()) | 1526 if (end.isNull()) |
1516 return false; | 1527 return false; |
1517 | 1528 |
1518 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR
Element(*nodeAfterInsertionPos) | 1529 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR
Element(*nodeAfterInsertionPos) |
1519 && shouldRemoveEndBR(nodeAfterInsertionPos.get(), VisiblePosition(positi
onBeforeNode(nodeAfterInsertionPos.get())))) | 1530 && shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos.get()), Visib
lePosition(positionBeforeNode(nodeAfterInsertionPos.get())))) |
1520 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); | 1531 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); |
1521 | 1532 |
1522 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); | 1533 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); |
1523 | 1534 |
1524 setEndingSelection(selectionAfterReplace); | 1535 setEndingSelection(selectionAfterReplace); |
1525 | 1536 |
1526 return true; | 1537 return true; |
1527 } | 1538 } |
1528 | 1539 |
1529 void ReplaceSelectionCommand::trace(Visitor* visitor) | 1540 void ReplaceSelectionCommand::trace(Visitor* visitor) |
1530 { | 1541 { |
1531 visitor->trace(m_startOfInsertedContent); | 1542 visitor->trace(m_startOfInsertedContent); |
1532 visitor->trace(m_endOfInsertedContent); | 1543 visitor->trace(m_endOfInsertedContent); |
1533 visitor->trace(m_insertionStyle); | 1544 visitor->trace(m_insertionStyle); |
1534 visitor->trace(m_documentFragment); | 1545 visitor->trace(m_documentFragment); |
1535 CompositeEditCommand::trace(visitor); | 1546 CompositeEditCommand::trace(visitor); |
1536 } | 1547 } |
1537 | 1548 |
1538 } // namespace blink | 1549 } // namespace blink |
OLD | NEW |