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

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

Issue 1295073002: Move serializer related files in core/editing/ related files into core/editing/serializers/ (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: 2015-08-17T15:24:31 Rebase for Source/web/WebPageSerializerImpl.cpp Created 5 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
« no previous file with comments | « Source/core/editing/Serialization.h ('k') | Source/core/editing/StyledMarkupAccumulator.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv ed.
3 * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved.
4 * Copyright (C) 2011 Igalia S.L.
5 * Copyright (C) 2011 Motorola Mobility. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "core/editing/Serialization.h"
31
32 #include "bindings/core/v8/ExceptionState.h"
33 #include "core/CSSValueKeywords.h"
34 #include "core/HTMLNames.h"
35 #include "core/css/CSSPrimitiveValue.h"
36 #include "core/css/CSSValue.h"
37 #include "core/css/StylePropertySet.h"
38 #include "core/dom/CDATASection.h"
39 #include "core/dom/ChildListMutationScope.h"
40 #include "core/dom/Comment.h"
41 #include "core/dom/ContextFeatures.h"
42 #include "core/dom/DocumentFragment.h"
43 #include "core/dom/ElementTraversal.h"
44 #include "core/dom/ExceptionCode.h"
45 #include "core/dom/NodeTraversal.h"
46 #include "core/dom/Range.h"
47 #include "core/editing/EditingStrategy.h"
48 #include "core/editing/EditingUtilities.h"
49 #include "core/editing/Editor.h"
50 #include "core/editing/MarkupAccumulator.h"
51 #include "core/editing/StyledMarkupSerializer.h"
52 #include "core/editing/VisibleSelection.h"
53 #include "core/editing/VisibleUnits.h"
54 #include "core/editing/iterators/TextIterator.h"
55 #include "core/frame/LocalFrame.h"
56 #include "core/html/HTMLAnchorElement.h"
57 #include "core/html/HTMLBRElement.h"
58 #include "core/html/HTMLBodyElement.h"
59 #include "core/html/HTMLDivElement.h"
60 #include "core/html/HTMLElement.h"
61 #include "core/html/HTMLQuoteElement.h"
62 #include "core/html/HTMLSpanElement.h"
63 #include "core/html/HTMLTableCellElement.h"
64 #include "core/html/HTMLTableElement.h"
65 #include "core/html/HTMLTextFormControlElement.h"
66 #include "core/layout/LayoutObject.h"
67 #include "platform/weborigin/KURL.h"
68 #include "wtf/StdLibExtras.h"
69 #include "wtf/text/StringBuilder.h"
70
71 namespace blink {
72
73 using namespace HTMLNames;
74
75 class AttributeChange {
76 ALLOW_ONLY_INLINE_ALLOCATION();
77 public:
78 AttributeChange()
79 : m_name(nullAtom, nullAtom, nullAtom)
80 {
81 }
82
83 AttributeChange(PassRefPtrWillBeRawPtr<Element> element, const QualifiedName & name, const String& value)
84 : m_element(element), m_name(name), m_value(value)
85 {
86 }
87
88 void apply()
89 {
90 m_element->setAttribute(m_name, AtomicString(m_value));
91 }
92
93 DEFINE_INLINE_TRACE()
94 {
95 visitor->trace(m_element);
96 }
97
98 private:
99 RefPtrWillBeMember<Element> m_element;
100 QualifiedName m_name;
101 String m_value;
102 };
103
104 } // namespace blink
105
106 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::AttributeChange);
107
108 namespace blink {
109
110 static void completeURLs(DocumentFragment& fragment, const String& baseURL)
111 {
112 WillBeHeapVector<AttributeChange> changes;
113
114 KURL parsedBaseURL(ParsedURLString, baseURL);
115
116 for (Element& element : ElementTraversal::descendantsOf(fragment)) {
117 AttributeCollection attributes = element.attributes();
118 // AttributeCollection::iterator end = attributes.end();
119 for (const auto& attribute : attributes) {
120 if (element.isURLAttribute(attribute) && !attribute.value().isEmpty( ))
121 changes.append(AttributeChange(&element, attribute.name(), KURL( parsedBaseURL, attribute.value()).string()));
122 }
123 }
124
125 for (auto& change : changes)
126 change.apply();
127 }
128
129 static bool isHTMLBlockElement(const Node* node)
130 {
131 ASSERT(node);
132 return isHTMLTableCellElement(*node)
133 || isNonTableCellHTMLBlockElement(node);
134 }
135
136 static HTMLElement* ancestorToRetainStructureAndAppearanceForBlock(Element* comm onAncestorBlock)
137 {
138 if (!commonAncestorBlock)
139 return 0;
140
141 if (commonAncestorBlock->hasTagName(tbodyTag) || isHTMLTableRowElement(*comm onAncestorBlock))
142 return Traversal<HTMLTableElement>::firstAncestor(*commonAncestorBlock);
143
144 if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
145 return toHTMLElement(commonAncestorBlock);
146
147 return 0;
148 }
149
150 static inline HTMLElement* ancestorToRetainStructureAndAppearance(Node* commonAn cestor)
151 {
152 return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonA ncestor));
153 }
154
155 static inline HTMLElement* ancestorToRetainStructureAndAppearanceWithNoLayoutObj ect(Node* commonAncestor)
156 {
157 HTMLElement* commonAncestorBlock = toHTMLElement(enclosingNodeOfType(firstPo sitionInOrBeforeNode(commonAncestor), isHTMLBlockElement));
158 return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock);
159 }
160
161 bool propertyMissingOrEqualToNone(StylePropertySet* style, CSSPropertyID propert yID)
162 {
163 if (!style)
164 return false;
165 RefPtrWillBeRawPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
166 if (!value)
167 return true;
168 if (!value->isPrimitiveValue())
169 return false;
170 return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone;
171 }
172
173 static bool isPresentationalHTMLElement(const Node* node)
174 {
175 if (!node->isHTMLElement())
176 return false;
177
178 const HTMLElement& element = toHTMLElement(*node);
179 return element.hasTagName(uTag) || element.hasTagName(sTag) || element.hasTa gName(strikeTag)
180 || element.hasTagName(iTag) || element.hasTagName(emTag) || element.hasT agName(bTag) || element.hasTagName(strongTag);
181 }
182
183 template<typename Strategy>
184 static HTMLElement* highestAncestorToWrapMarkup(const PositionAlgorithm<Strategy >& startPosition, const PositionAlgorithm<Strategy>& endPosition, EAnnotateForIn terchange shouldAnnotate, Node* constrainingAncestor)
185 {
186 Node* firstNode = startPosition.nodeAsRangeFirstNode();
187 // For compatibility reason, we use container node of start and end
188 // positions rather than first node and last node in selection.
189 Node* commonAncestor = Strategy::commonAncestor(*startPosition.computeContai nerNode(), *endPosition.computeContainerNode());
190 ASSERT(commonAncestor);
191 HTMLElement* specialCommonAncestor = nullptr;
192 if (shouldAnnotate == AnnotateForInterchange) {
193 // Include ancestors that aren't completely inside the range but are req uired to retain
194 // the structure and appearance of the copied markup.
195 specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAnc estor);
196 if (Node* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNo de(firstNode), isListItem)) {
197 EphemeralRangeTemplate<Strategy> markupRange = EphemeralRangeTemplat e<Strategy>(startPosition, endPosition);
198 EphemeralRangeTemplate<Strategy> nodeRange = VisibleSelection::norma lizeRange(EphemeralRangeTemplate<Strategy>::rangeOfContents(*parentListNode));
199 if (nodeRange == markupRange) {
200 ContainerNode* ancestor = parentListNode->parentNode();
201 while (ancestor && !isHTMLListElement(ancestor))
202 ancestor = ancestor->parentNode();
203 specialCommonAncestor = toHTMLElement(ancestor);
204 }
205 }
206
207 // Retain the Mail quote level by including all ancestor mail block quot es.
208 if (HTMLQuoteElement* highestMailBlockquote = toHTMLQuoteElement(highest EnclosingNodeOfType(firstPositionInOrBeforeNode(firstNode), isMailHTMLBlockquote Element, CanCrossEditingBoundary)))
209 specialCommonAncestor = highestMailBlockquote;
210 }
211
212 Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : common Ancestor;
213 if (checkAncestor->layoutObject()) {
214 HTMLElement* newSpecialCommonAncestor = toHTMLElement(highestEnclosingNo deOfType(firstPositionInNode(checkAncestor), &isPresentationalHTMLElement, CanCr ossEditingBoundary, constrainingAncestor));
215 if (newSpecialCommonAncestor)
216 specialCommonAncestor = newSpecialCommonAncestor;
217 }
218
219 // If a single tab is selected, commonAncestor will be a text node inside a tab span.
220 // If two or more tabs are selected, commonAncestor will be the tab span.
221 // In either case, if there is a specialCommonAncestor already, it will nece ssarily be above
222 // any tab span that needs to be included.
223 if (!specialCommonAncestor && isTabHTMLSpanElementTextNode(commonAncestor))
224 specialCommonAncestor = toHTMLSpanElement(Strategy::parent(*commonAncest or));
225 if (!specialCommonAncestor && isTabHTMLSpanElement(commonAncestor))
226 specialCommonAncestor = toHTMLSpanElement(commonAncestor);
227
228 if (HTMLAnchorElement* enclosingAnchor = toHTMLAnchorElement(enclosingElemen tWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : com monAncestor), aTag)))
229 specialCommonAncestor = enclosingAnchor;
230
231 return specialCommonAncestor;
232 }
233
234 template <typename Strategy>
235 class CreateMarkupAlgorithm {
236 public:
237 static String createMarkup(const PositionAlgorithm<Strategy>& startPosition, const PositionAlgorithm<Strategy>& endPosition, EAnnotateForInterchange shouldA nnotate = DoNotAnnotateForInterchange, ConvertBlocksToInlines = ConvertBlocksToI nlines::NotConvert, EAbsoluteURLs shouldResolveURLs = DoNotResolveURLs, Node* co nstrainingAncestor = nullptr);
238 };
239
240 // FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForIntercha nge?
241 // FIXME: At least, annotation and style info should probably not be included in range.markupString()
242 template <typename Strategy>
243 String CreateMarkupAlgorithm<Strategy>::createMarkup(const PositionAlgorithm<Str ategy>& startPosition, const PositionAlgorithm<Strategy>& endPosition,
244 EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlines convertBlocks ToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor)
245 {
246 ASSERT(startPosition.isNotNull());
247 ASSERT(endPosition.isNotNull());
248 ASSERT(startPosition.compareTo(endPosition) <= 0);
249
250 bool collapsed = startPosition == endPosition;
251 if (collapsed)
252 return emptyString();
253 Node* commonAncestor = Strategy::commonAncestor(*startPosition.computeContai nerNode(), *endPosition.computeContainerNode());
254 if (!commonAncestor)
255 return emptyString();
256
257 Document* document = startPosition.document();
258 document->updateLayoutIgnorePendingStylesheets();
259
260 HTMLElement* specialCommonAncestor = highestAncestorToWrapMarkup<Strategy>(s tartPosition, endPosition, shouldAnnotate, constrainingAncestor);
261 StyledMarkupSerializer<Strategy> serializer(shouldResolveURLs, shouldAnnotat e, startPosition, endPosition, specialCommonAncestor, convertBlocksToInlines);
262 return serializer.createMarkup();
263 }
264
265 String createMarkup(const Position& startPosition, const Position& endPosition, EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlines convertBlocksToIn lines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor)
266 {
267 ASSERT(startPosition.compareTo(endPosition) <= 0);
268 return CreateMarkupAlgorithm<EditingStrategy>::createMarkup(startPosition, e ndPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs, constrain ingAncestor);
269 }
270
271 String createMarkup(const PositionInComposedTree& startPosition, const PositionI nComposedTree& endPosition, EAnnotateForInterchange shouldAnnotate, ConvertBlock sToInlines convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constr ainingAncestor)
272 {
273 ASSERT(startPosition.compareTo(endPosition) <= 0);
274 return CreateMarkupAlgorithm<EditingInComposedTreeStrategy>::createMarkup(st artPosition, endPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveU RLs, constrainingAncestor);
275 }
276
277 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromMarkup(Document& docu ment, const String& markup, const String& baseURL, ParserContentPolicy parserCon tentPolicy)
278 {
279 // We use a fake body element here to trick the HTML parser to using the InB ody insertion mode.
280 RefPtrWillBeRawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(docum ent);
281 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc ument);
282
283 fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy);
284
285 if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseU RL())
286 completeURLs(*fragment, baseURL);
287
288 return fragment.release();
289 }
290
291 static const char fragmentMarkerTag[] = "webkit-fragment-marker";
292
293 static bool findNodesSurroundingContext(DocumentFragment* fragment, RefPtrWillBe RawPtr<Comment>& nodeBeforeContext, RefPtrWillBeRawPtr<Comment>& nodeAfterContex t)
294 {
295 for (Node& node : NodeTraversal::startsAt(fragment->firstChild())) {
296 if (node.nodeType() == Node::COMMENT_NODE && toComment(node).data() == f ragmentMarkerTag) {
297 if (!nodeBeforeContext) {
298 nodeBeforeContext = &toComment(node);
299 } else {
300 nodeAfterContext = &toComment(node);
301 return true;
302 }
303 }
304 }
305 return false;
306 }
307
308 static void trimFragment(DocumentFragment* fragment, Comment* nodeBeforeContext, Comment* nodeAfterContext)
309 {
310 RefPtrWillBeRawPtr<Node> next = nullptr;
311 for (RefPtrWillBeRawPtr<Node> node = fragment->firstChild(); node; node = ne xt) {
312 if (nodeBeforeContext->isDescendantOf(node.get())) {
313 next = NodeTraversal::next(*node);
314 continue;
315 }
316 next = NodeTraversal::nextSkippingChildren(*node);
317 ASSERT(!node->contains(nodeAfterContext));
318 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
319 if (nodeBeforeContext == node)
320 break;
321 }
322
323 ASSERT(nodeAfterContext->parentNode());
324 for (RefPtrWillBeRawPtr<Node> node = nodeAfterContext; node; node = next) {
325 next = NodeTraversal::nextSkippingChildren(*node);
326 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
327 }
328 }
329
330 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromMarkupWithContext(Doc ument& document, const String& markup, unsigned fragmentStart, unsigned fragment End,
331 const String& baseURL, ParserContentPolicy parserContentPolicy)
332 {
333 // FIXME: Need to handle the case where the markup already contains these ma rkers.
334
335 StringBuilder taggedMarkup;
336 taggedMarkup.append(markup.left(fragmentStart));
337 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag);
338 taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentSt art));
339 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag);
340 taggedMarkup.append(markup.substring(fragmentEnd));
341
342 RefPtrWillBeRawPtr<DocumentFragment> taggedFragment = createFragmentFromMark up(document, taggedMarkup.toString(), baseURL, parserContentPolicy);
343
344 RefPtrWillBeRawPtr<Comment> nodeBeforeContext = nullptr;
345 RefPtrWillBeRawPtr<Comment> nodeAfterContext = nullptr;
346 if (!findNodesSurroundingContext(taggedFragment.get(), nodeBeforeContext, no deAfterContext))
347 return nullptr;
348
349 RefPtrWillBeRawPtr<Document> taggedDocument = Document::create();
350 taggedDocument->setContextFeatures(document.contextFeatures());
351
352 RefPtrWillBeRawPtr<Element> root = Element::create(QualifiedName::null(), ta ggedDocument.get());
353 root->appendChild(taggedFragment.get());
354 taggedDocument->appendChild(root);
355
356 RefPtrWillBeRawPtr<Range> range = Range::create(*taggedDocument.get(),
357 positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(),
358 positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent());
359
360 Node* commonAncestor = range->commonAncestorContainer();
361 HTMLElement* specialCommonAncestor = ancestorToRetainStructureAndAppearanceW ithNoLayoutObject(commonAncestor);
362
363 // When there's a special common ancestor outside of the fragment, we must i nclude it as well to
364 // preserve the structure and appearance of the fragment. For example, if th e fragment contains
365 // TD, we need to include the enclosing TABLE tag as well.
366 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc ument);
367 if (specialCommonAncestor)
368 fragment->appendChild(specialCommonAncestor);
369 else
370 fragment->parserTakeAllChildrenFrom(toContainerNode(*commonAncestor));
371
372 trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get() );
373
374 return fragment;
375 }
376
377 String createMarkup(const Node* node, EChildrenOnly childrenOnly, EAbsoluteURLs shouldResolveURLs)
378 {
379 if (!node)
380 return "";
381
382 MarkupAccumulator accumulator(shouldResolveURLs);
383 return serializeNodes<EditingStrategy>(accumulator, const_cast<Node&>(*node) , childrenOnly);
384 }
385
386 static void fillContainerFromString(ContainerNode* paragraph, const String& stri ng)
387 {
388 Document& document = paragraph->document();
389
390 if (string.isEmpty()) {
391 paragraph->appendChild(createBlockPlaceholderElement(document));
392 return;
393 }
394
395 ASSERT(string.find('\n') == kNotFound);
396
397 Vector<String> tabList;
398 string.split('\t', true, tabList);
399 StringBuilder tabText;
400 bool first = true;
401 size_t numEntries = tabList.size();
402 for (size_t i = 0; i < numEntries; ++i) {
403 const String& s = tabList[i];
404
405 // append the non-tab textual part
406 if (!s.isEmpty()) {
407 if (!tabText.isEmpty()) {
408 paragraph->appendChild(createTabSpanElement(document, tabText.to String()));
409 tabText.clear();
410 }
411 RefPtrWillBeRawPtr<Text> textNode = document.createTextNode(stringWi thRebalancedWhitespace(s, first, i + 1 == numEntries));
412 paragraph->appendChild(textNode.release());
413 }
414
415 // there is a tab after every entry, except the last entry
416 // (if the last character is a tab, the list gets an extra empty entry)
417 if (i + 1 != numEntries)
418 tabText.append('\t');
419 else if (!tabText.isEmpty())
420 paragraph->appendChild(createTabSpanElement(document, tabText.toStri ng()));
421
422 first = false;
423 }
424 }
425
426 bool isPlainTextMarkup(Node* node)
427 {
428 ASSERT(node);
429 if (!isHTMLDivElement(*node))
430 return false;
431
432 HTMLDivElement& element = toHTMLDivElement(*node);
433 if (!element.hasAttributes())
434 return false;
435
436 if (element.hasOneChild())
437 return element.firstChild()->isTextNode() || element.firstChild()->hasCh ildren();
438
439 return element.hasChildCount(2) && isTabHTMLSpanElementTextNode(element.firs tChild()->firstChild()) && element.lastChild()->isTextNode();
440 }
441
442 static bool shouldPreserveNewline(const EphemeralRange& range)
443 {
444 if (Node* node = range.startPosition().nodeAsRangeFirstNode()) {
445 if (LayoutObject* layoutObject = node->layoutObject())
446 return layoutObject->style()->preserveNewline();
447 }
448
449 if (Node* node = range.startPosition().anchorNode()) {
450 if (LayoutObject* layoutObject = node->layoutObject())
451 return layoutObject->style()->preserveNewline();
452 }
453
454 return false;
455 }
456
457 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
458 {
459 return createFragmentFromText(EphemeralRange(context), text);
460 }
461
462 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromText(const EphemeralR ange& context, const String& text)
463 {
464 if (context.isNull())
465 return nullptr;
466
467 Document& document = context.document();
468 RefPtrWillBeRawPtr<DocumentFragment> fragment = document.createDocumentFragm ent();
469
470 if (text.isEmpty())
471 return fragment.release();
472
473 String string = text;
474 string.replace("\r\n", "\n");
475 string.replace('\r', '\n');
476
477 if (shouldPreserveNewline(context)) {
478 fragment->appendChild(document.createTextNode(string));
479 if (string.endsWith('\n')) {
480 RefPtrWillBeRawPtr<HTMLBRElement> element = createBreakElement(docum ent);
481 element->setAttribute(classAttr, AppleInterchangeNewline);
482 fragment->appendChild(element.release());
483 }
484 return fragment.release();
485 }
486
487 // A string with no newlines gets added inline, rather than being put into a paragraph.
488 if (string.find('\n') == kNotFound) {
489 fillContainerFromString(fragment.get(), string);
490 return fragment.release();
491 }
492
493 // Break string into paragraphs. Extra line breaks turn into empty paragraph s.
494 Element* block = enclosingBlock(context.startPosition().nodeAsRangeFirstNode ());
495 bool useClonesOfEnclosingBlock = block
496 && !isHTMLBodyElement(*block)
497 && !isHTMLHtmlElement(*block)
498 && block != editableRootForPosition(context.startPosition());
499 bool useLineBreak = enclosingTextFormControl(context.startPosition());
500
501 Vector<String> list;
502 string.split('\n', true, list); // true gets us empty strings in the list
503 size_t numLines = list.size();
504 for (size_t i = 0; i < numLines; ++i) {
505 const String& s = list[i];
506
507 RefPtrWillBeRawPtr<Element> element = nullptr;
508 if (s.isEmpty() && i + 1 == numLines) {
509 // For last line, use the "magic BR" rather than a P.
510 element = createBreakElement(document);
511 element->setAttribute(classAttr, AppleInterchangeNewline);
512 } else if (useLineBreak) {
513 element = createBreakElement(document);
514 fillContainerFromString(fragment.get(), s);
515 } else {
516 if (useClonesOfEnclosingBlock)
517 element = block->cloneElementWithoutChildren();
518 else
519 element = createDefaultParagraphElement(document);
520 fillContainerFromString(element.get(), s);
521 }
522 fragment->appendChild(element.release());
523 }
524 return fragment.release();
525 }
526
527 String urlToMarkup(const KURL& url, const String& title)
528 {
529 StringBuilder markup;
530 markup.appendLiteral("<a href=\"");
531 markup.append(url.string());
532 markup.appendLiteral("\">");
533 MarkupFormatter::appendCharactersReplacingEntities(markup, title, 0, title.l ength(), EntityMaskInPCDATA);
534 markup.appendLiteral("</a>");
535 return markup.toString();
536 }
537
538 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentForInnerOuterHTML(const S tring& markup, Element* contextElement, ParserContentPolicy parserContentPolicy, const char* method, ExceptionState& exceptionState)
539 {
540 ASSERT(contextElement);
541 Document& document = isHTMLTemplateElement(*contextElement) ? contextElement ->document().ensureTemplateDocument() : contextElement->document();
542 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc ument);
543
544 if (document.isHTMLDocument()) {
545 fragment->parseHTML(markup, contextElement, parserContentPolicy);
546 return fragment;
547 }
548
549 bool wasValid = fragment->parseXML(markup, contextElement, parserContentPoli cy);
550 if (!wasValid) {
551 exceptionState.throwDOMException(SyntaxError, "The provided markup is in valid XML, and therefore cannot be inserted into an XML document.");
552 return nullptr;
553 }
554 return fragment.release();
555 }
556
557 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentForTransformToFragment(co nst String& sourceString, const String& sourceMIMEType, Document& outputDoc)
558 {
559 RefPtrWillBeRawPtr<DocumentFragment> fragment = outputDoc.createDocumentFrag ment();
560
561 if (sourceMIMEType == "text/html") {
562 // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
563 // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
564 // Unfortunately, that's an implementation detail of the parser.
565 // We achieve that effect here by passing in a fake body element as cont ext for the fragment.
566 RefPtrWillBeRawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(o utputDoc);
567 fragment->parseHTML(sourceString, fakeBody.get());
568 } else if (sourceMIMEType == "text/plain") {
569 fragment->parserAppendChild(Text::create(outputDoc, sourceString));
570 } else {
571 bool successfulParse = fragment->parseXML(sourceString, 0);
572 if (!successfulParse)
573 return nullptr;
574 }
575
576 // FIXME: Do we need to mess with URLs here?
577
578 return fragment.release();
579 }
580
581 static inline void removeElementPreservingChildren(PassRefPtrWillBeRawPtr<Docume ntFragment> fragment, HTMLElement* element)
582 {
583 RefPtrWillBeRawPtr<Node> nextChild = nullptr;
584 for (RefPtrWillBeRawPtr<Node> child = element->firstChild(); child; child = nextChild) {
585 nextChild = child->nextSibling();
586 element->removeChild(child.get());
587 fragment->insertBefore(child, element);
588 }
589 fragment->removeChild(element);
590 }
591
592 static inline bool isSupportedContainer(Element* element)
593 {
594 ASSERT(element);
595 if (!element->isHTMLElement())
596 return true;
597
598 HTMLElement& htmlElement = toHTMLElement(*element);
599 if (htmlElement.hasTagName(colTag) || htmlElement.hasTagName(colgroupTag) || htmlElement.hasTagName(framesetTag)
600 || htmlElement.hasTagName(headTag) || htmlElement.hasTagName(styleTag) | | htmlElement.hasTagName(titleTag)) {
601 return false;
602 }
603 return !htmlElement.ieForbidsInsertHTML();
604 }
605
606 PassRefPtrWillBeRawPtr<DocumentFragment> createContextualFragment(const String& markup, Element* element, ParserContentPolicy parserContentPolicy, ExceptionStat e& exceptionState)
607 {
608 ASSERT(element);
609 if (!isSupportedContainer(element)) {
610 exceptionState.throwDOMException(NotSupportedError, "The range's contain er is '" + element->localName() + "', which is not supported.");
611 return nullptr;
612 }
613
614 RefPtrWillBeRawPtr<DocumentFragment> fragment = createFragmentForInnerOuterH TML(markup, element, parserContentPolicy, "createContextualFragment", exceptionS tate);
615 if (!fragment)
616 return nullptr;
617
618 // We need to pop <html> and <body> elements and remove <head> to
619 // accommodate folks passing complete HTML documents to make the
620 // child of an element.
621
622 RefPtrWillBeRawPtr<Node> nextNode = nullptr;
623 for (RefPtrWillBeRawPtr<Node> node = fragment->firstChild(); node; node = ne xtNode) {
624 nextNode = node->nextSibling();
625 if (isHTMLHtmlElement(*node) || isHTMLHeadElement(*node) || isHTMLBodyEl ement(*node)) {
626 HTMLElement* element = toHTMLElement(node);
627 if (Node* firstChild = element->firstChild())
628 nextNode = firstChild;
629 removeElementPreservingChildren(fragment, element);
630 }
631 }
632 return fragment.release();
633 }
634
635 void replaceChildrenWithFragment(ContainerNode* container, PassRefPtrWillBeRawPt r<DocumentFragment> fragment, ExceptionState& exceptionState)
636 {
637 ASSERT(container);
638 RefPtrWillBeRawPtr<ContainerNode> containerNode(container);
639
640 ChildListMutationScope mutation(*containerNode);
641
642 if (!fragment->firstChild()) {
643 containerNode->removeChildren();
644 return;
645 }
646
647 // FIXME: This is wrong if containerNode->firstChild() has more than one ref !
648 if (containerNode->hasOneTextChild() && fragment->hasOneTextChild()) {
649 toText(containerNode->firstChild())->setData(toText(fragment->firstChild ())->data());
650 return;
651 }
652
653 // FIXME: No need to replace the child it is a text node and its contents ar e already == text.
654 if (containerNode->hasOneChild()) {
655 containerNode->replaceChild(fragment, containerNode->firstChild(), excep tionState);
656 return;
657 }
658
659 containerNode->removeChildren();
660 containerNode->appendChild(fragment, exceptionState);
661 }
662
663 void replaceChildrenWithText(ContainerNode* container, const String& text, Excep tionState& exceptionState)
664 {
665 ASSERT(container);
666 RefPtrWillBeRawPtr<ContainerNode> containerNode(container);
667
668 ChildListMutationScope mutation(*containerNode);
669
670 // FIXME: This is wrong if containerNode->firstChild() has more than one ref ! Example:
671 // <div>foo</div>
672 // <script>
673 // var oldText = div.firstChild;
674 // console.log(oldText.data); // foo
675 // div.innerText = "bar";
676 // console.log(oldText.data); // bar!?!
677 // </script>
678 // I believe this is an intentional benchmark cheat from years ago.
679 // We should re-visit if we actually want this still.
680 if (containerNode->hasOneTextChild()) {
681 toText(containerNode->firstChild())->setData(text);
682 return;
683 }
684
685 // NOTE: This method currently always creates a text node, even if that text node will be empty.
686 RefPtrWillBeRawPtr<Text> textNode = Text::create(containerNode->document(), text);
687
688 // FIXME: No need to replace the child it is a text node and its contents ar e already == text.
689 if (containerNode->hasOneChild()) {
690 containerNode->replaceChild(textNode.release(), containerNode->firstChil d(), exceptionState);
691 return;
692 }
693
694 containerNode->removeChildren();
695 containerNode->appendChild(textNode.release(), exceptionState);
696 }
697
698 void mergeWithNextTextNode(Text* textNode, ExceptionState& exceptionState)
699 {
700 ASSERT(textNode);
701 Node* next = textNode->nextSibling();
702 if (!next || !next->isTextNode())
703 return;
704
705 RefPtrWillBeRawPtr<Text> textNext = toText(next);
706 textNode->appendData(textNext->data());
707 if (textNext->parentNode()) // Might have been removed by mutation event.
708 textNext->remove(exceptionState);
709 }
710
711 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingStrategy>;
712 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingInComposedTreeS trategy>;
713
714 }
OLDNEW
« no previous file with comments | « Source/core/editing/Serialization.h ('k') | Source/core/editing/StyledMarkupAccumulator.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698