| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. | 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. | 3 * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved. |
| 4 * Copyright (C) 2011 Igalia S.L. | 4 * Copyright (C) 2011 Igalia S.L. |
| 5 * Copyright (C) 2011 Motorola Mobility. All rights reserved. | 5 * Copyright (C) 2011 Motorola Mobility. All rights reserved. |
| 6 * | 6 * |
| 7 * Redistribution and use in source and binary forms, with or without | 7 * Redistribution and use in source and binary forms, with or without |
| 8 * modification, are permitted provided that the following conditions | 8 * modification, are permitted provided that the following conditions |
| 9 * are met: | 9 * are met: |
| 10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 72 using namespace HTMLNames; | 72 using namespace HTMLNames; |
| 73 | 73 |
| 74 class AttributeChange { | 74 class AttributeChange { |
| 75 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); | 75 DISALLOW_NEW_EXCEPT_PLACEMENT_NEW(); |
| 76 public: | 76 public: |
| 77 AttributeChange() | 77 AttributeChange() |
| 78 : m_name(nullAtom, nullAtom, nullAtom) | 78 : m_name(nullAtom, nullAtom, nullAtom) |
| 79 { | 79 { |
| 80 } | 80 } |
| 81 | 81 |
| 82 AttributeChange(PassRefPtrWillBeRawPtr<Element> element, const QualifiedName
& name, const String& value) | 82 AttributeChange(RawPtr<Element> element, const QualifiedName& name, const St
ring& value) |
| 83 : m_element(element), m_name(name), m_value(value) | 83 : m_element(element), m_name(name), m_value(value) |
| 84 { | 84 { |
| 85 } | 85 } |
| 86 | 86 |
| 87 void apply() | 87 void apply() |
| 88 { | 88 { |
| 89 m_element->setAttribute(m_name, AtomicString(m_value)); | 89 m_element->setAttribute(m_name, AtomicString(m_value)); |
| 90 } | 90 } |
| 91 | 91 |
| 92 DEFINE_INLINE_TRACE() | 92 DEFINE_INLINE_TRACE() |
| 93 { | 93 { |
| 94 visitor->trace(m_element); | 94 visitor->trace(m_element); |
| 95 } | 95 } |
| 96 | 96 |
| 97 private: | 97 private: |
| 98 RefPtrWillBeMember<Element> m_element; | 98 Member<Element> m_element; |
| 99 QualifiedName m_name; | 99 QualifiedName m_name; |
| 100 String m_value; | 100 String m_value; |
| 101 }; | 101 }; |
| 102 | 102 |
| 103 } // namespace blink | 103 } // namespace blink |
| 104 | 104 |
| 105 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::AttributeChange); | 105 WTF_ALLOW_INIT_WITH_MEM_FUNCTIONS(blink::AttributeChange); |
| 106 | 106 |
| 107 namespace blink { | 107 namespace blink { |
| 108 | 108 |
| 109 static void completeURLs(DocumentFragment& fragment, const String& baseURL) | 109 static void completeURLs(DocumentFragment& fragment, const String& baseURL) |
| 110 { | 110 { |
| 111 WillBeHeapVector<AttributeChange> changes; | 111 HeapVector<AttributeChange> changes; |
| 112 | 112 |
| 113 KURL parsedBaseURL(ParsedURLString, baseURL); | 113 KURL parsedBaseURL(ParsedURLString, baseURL); |
| 114 | 114 |
| 115 for (Element& element : ElementTraversal::descendantsOf(fragment)) { | 115 for (Element& element : ElementTraversal::descendantsOf(fragment)) { |
| 116 AttributeCollection attributes = element.attributes(); | 116 AttributeCollection attributes = element.attributes(); |
| 117 // AttributeCollection::iterator end = attributes.end(); | 117 // AttributeCollection::iterator end = attributes.end(); |
| 118 for (const auto& attribute : attributes) { | 118 for (const auto& attribute : attributes) { |
| 119 if (element.isURLAttribute(attribute) && !attribute.value().isEmpty(
)) | 119 if (element.isURLAttribute(attribute) && !attribute.value().isEmpty(
)) |
| 120 changes.append(AttributeChange(&element, attribute.name(), KURL(
parsedBaseURL, attribute.value()).getString())); | 120 changes.append(AttributeChange(&element, attribute.name(), KURL(
parsedBaseURL, attribute.value()).getString())); |
| 121 } | 121 } |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 static inline HTMLElement* ancestorToRetainStructureAndAppearanceWithNoLayoutObj
ect(Node* commonAncestor) | 154 static inline HTMLElement* ancestorToRetainStructureAndAppearanceWithNoLayoutObj
ect(Node* commonAncestor) |
| 155 { | 155 { |
| 156 HTMLElement* commonAncestorBlock = toHTMLElement(enclosingNodeOfType(firstPo
sitionInOrBeforeNode(commonAncestor), isHTMLBlockElement)); | 156 HTMLElement* commonAncestorBlock = toHTMLElement(enclosingNodeOfType(firstPo
sitionInOrBeforeNode(commonAncestor), isHTMLBlockElement)); |
| 157 return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock); | 157 return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock); |
| 158 } | 158 } |
| 159 | 159 |
| 160 bool propertyMissingOrEqualToNone(StylePropertySet* style, CSSPropertyID propert
yID) | 160 bool propertyMissingOrEqualToNone(StylePropertySet* style, CSSPropertyID propert
yID) |
| 161 { | 161 { |
| 162 if (!style) | 162 if (!style) |
| 163 return false; | 163 return false; |
| 164 RefPtrWillBeRawPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); | 164 RawPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); |
| 165 if (!value) | 165 if (!value) |
| 166 return true; | 166 return true; |
| 167 if (!value->isPrimitiveValue()) | 167 if (!value->isPrimitiveValue()) |
| 168 return false; | 168 return false; |
| 169 return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone; | 169 return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone; |
| 170 } | 170 } |
| 171 | 171 |
| 172 static bool isPresentationalHTMLElement(const Node* node) | 172 static bool isPresentationalHTMLElement(const Node* node) |
| 173 { | 173 { |
| 174 if (!node->isHTMLElement()) | 174 if (!node->isHTMLElement()) |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 String createMarkup(const Position& startPosition, const Position& endPosition,
EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlines convertBlocksToIn
lines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor) | 265 String createMarkup(const Position& startPosition, const Position& endPosition,
EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlines convertBlocksToIn
lines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor) |
| 266 { | 266 { |
| 267 return CreateMarkupAlgorithm<EditingStrategy>::createMarkup(startPosition, e
ndPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs, constrain
ingAncestor); | 267 return CreateMarkupAlgorithm<EditingStrategy>::createMarkup(startPosition, e
ndPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs, constrain
ingAncestor); |
| 268 } | 268 } |
| 269 | 269 |
| 270 String createMarkup(const PositionInFlatTree& startPosition, const PositionInFla
tTree& endPosition, EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlin
es convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAn
cestor) | 270 String createMarkup(const PositionInFlatTree& startPosition, const PositionInFla
tTree& endPosition, EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlin
es convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAn
cestor) |
| 271 { | 271 { |
| 272 return CreateMarkupAlgorithm<EditingInFlatTreeStrategy>::createMarkup(startP
osition, endPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs,
constrainingAncestor); | 272 return CreateMarkupAlgorithm<EditingInFlatTreeStrategy>::createMarkup(startP
osition, endPosition, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs,
constrainingAncestor); |
| 273 } | 273 } |
| 274 | 274 |
| 275 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromMarkup(Document& docu
ment, const String& markup, const String& baseURL, ParserContentPolicy parserCon
tentPolicy) | 275 RawPtr<DocumentFragment> createFragmentFromMarkup(Document& document, const Stri
ng& markup, const String& baseURL, ParserContentPolicy parserContentPolicy) |
| 276 { | 276 { |
| 277 // We use a fake body element here to trick the HTML parser to using the InB
ody insertion mode. | 277 // We use a fake body element here to trick the HTML parser to using the InB
ody insertion mode. |
| 278 RefPtrWillBeRawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(docum
ent); | 278 RawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document); |
| 279 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc
ument); | 279 RawPtr<DocumentFragment> fragment = DocumentFragment::create(document); |
| 280 | 280 |
| 281 fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy); | 281 fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy); |
| 282 | 282 |
| 283 if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseU
RL()) | 283 if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseU
RL()) |
| 284 completeURLs(*fragment, baseURL); | 284 completeURLs(*fragment, baseURL); |
| 285 | 285 |
| 286 return fragment.release(); | 286 return fragment.release(); |
| 287 } | 287 } |
| 288 | 288 |
| 289 static const char fragmentMarkerTag[] = "webkit-fragment-marker"; | 289 static const char fragmentMarkerTag[] = "webkit-fragment-marker"; |
| 290 | 290 |
| 291 static bool findNodesSurroundingContext(DocumentFragment* fragment, RefPtrWillBe
RawPtr<Comment>& nodeBeforeContext, RefPtrWillBeRawPtr<Comment>& nodeAfterContex
t) | 291 static bool findNodesSurroundingContext(DocumentFragment* fragment, RawPtr<Comme
nt>& nodeBeforeContext, RawPtr<Comment>& nodeAfterContext) |
| 292 { | 292 { |
| 293 for (Node& node : NodeTraversal::startsAt(fragment->firstChild())) { | 293 for (Node& node : NodeTraversal::startsAt(fragment->firstChild())) { |
| 294 if (node.getNodeType() == Node::COMMENT_NODE && toComment(node).data() =
= fragmentMarkerTag) { | 294 if (node.getNodeType() == Node::COMMENT_NODE && toComment(node).data() =
= fragmentMarkerTag) { |
| 295 if (!nodeBeforeContext) { | 295 if (!nodeBeforeContext) { |
| 296 nodeBeforeContext = &toComment(node); | 296 nodeBeforeContext = &toComment(node); |
| 297 } else { | 297 } else { |
| 298 nodeAfterContext = &toComment(node); | 298 nodeAfterContext = &toComment(node); |
| 299 return true; | 299 return true; |
| 300 } | 300 } |
| 301 } | 301 } |
| 302 } | 302 } |
| 303 return false; | 303 return false; |
| 304 } | 304 } |
| 305 | 305 |
| 306 static void trimFragment(DocumentFragment* fragment, Comment* nodeBeforeContext,
Comment* nodeAfterContext) | 306 static void trimFragment(DocumentFragment* fragment, Comment* nodeBeforeContext,
Comment* nodeAfterContext) |
| 307 { | 307 { |
| 308 RefPtrWillBeRawPtr<Node> next = nullptr; | 308 RawPtr<Node> next = nullptr; |
| 309 for (RefPtrWillBeRawPtr<Node> node = fragment->firstChild(); node; node = ne
xt) { | 309 for (RawPtr<Node> node = fragment->firstChild(); node; node = next) { |
| 310 if (nodeBeforeContext->isDescendantOf(node.get())) { | 310 if (nodeBeforeContext->isDescendantOf(node.get())) { |
| 311 next = NodeTraversal::next(*node); | 311 next = NodeTraversal::next(*node); |
| 312 continue; | 312 continue; |
| 313 } | 313 } |
| 314 next = NodeTraversal::nextSkippingChildren(*node); | 314 next = NodeTraversal::nextSkippingChildren(*node); |
| 315 ASSERT(!node->contains(nodeAfterContext)); | 315 ASSERT(!node->contains(nodeAfterContext)); |
| 316 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION); | 316 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION); |
| 317 if (nodeBeforeContext == node) | 317 if (nodeBeforeContext == node) |
| 318 break; | 318 break; |
| 319 } | 319 } |
| 320 | 320 |
| 321 ASSERT(nodeAfterContext->parentNode()); | 321 ASSERT(nodeAfterContext->parentNode()); |
| 322 for (RefPtrWillBeRawPtr<Node> node = nodeAfterContext; node; node = next) { | 322 for (RawPtr<Node> node = nodeAfterContext; node; node = next) { |
| 323 next = NodeTraversal::nextSkippingChildren(*node); | 323 next = NodeTraversal::nextSkippingChildren(*node); |
| 324 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION); | 324 node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION); |
| 325 } | 325 } |
| 326 } | 326 } |
| 327 | 327 |
| 328 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromMarkupWithContext(Doc
ument& document, const String& markup, unsigned fragmentStart, unsigned fragment
End, | 328 RawPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document& document,
const String& markup, unsigned fragmentStart, unsigned fragmentEnd, |
| 329 const String& baseURL, ParserContentPolicy parserContentPolicy) | 329 const String& baseURL, ParserContentPolicy parserContentPolicy) |
| 330 { | 330 { |
| 331 // FIXME: Need to handle the case where the markup already contains these ma
rkers. | 331 // FIXME: Need to handle the case where the markup already contains these ma
rkers. |
| 332 | 332 |
| 333 StringBuilder taggedMarkup; | 333 StringBuilder taggedMarkup; |
| 334 taggedMarkup.append(markup.left(fragmentStart)); | 334 taggedMarkup.append(markup.left(fragmentStart)); |
| 335 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); | 335 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); |
| 336 taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentSt
art)); | 336 taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentSt
art)); |
| 337 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); | 337 MarkupFormatter::appendComment(taggedMarkup, fragmentMarkerTag); |
| 338 taggedMarkup.append(markup.substring(fragmentEnd)); | 338 taggedMarkup.append(markup.substring(fragmentEnd)); |
| 339 | 339 |
| 340 RefPtrWillBeRawPtr<DocumentFragment> taggedFragment = createFragmentFromMark
up(document, taggedMarkup.toString(), baseURL, parserContentPolicy); | 340 RawPtr<DocumentFragment> taggedFragment = createFragmentFromMarkup(document,
taggedMarkup.toString(), baseURL, parserContentPolicy); |
| 341 | 341 |
| 342 RefPtrWillBeRawPtr<Comment> nodeBeforeContext = nullptr; | 342 RawPtr<Comment> nodeBeforeContext = nullptr; |
| 343 RefPtrWillBeRawPtr<Comment> nodeAfterContext = nullptr; | 343 RawPtr<Comment> nodeAfterContext = nullptr; |
| 344 if (!findNodesSurroundingContext(taggedFragment.get(), nodeBeforeContext, no
deAfterContext)) | 344 if (!findNodesSurroundingContext(taggedFragment.get(), nodeBeforeContext, no
deAfterContext)) |
| 345 return nullptr; | 345 return nullptr; |
| 346 | 346 |
| 347 RefPtrWillBeRawPtr<Document> taggedDocument = Document::create(); | 347 RawPtr<Document> taggedDocument = Document::create(); |
| 348 taggedDocument->setContextFeatures(document.contextFeatures()); | 348 taggedDocument->setContextFeatures(document.contextFeatures()); |
| 349 | 349 |
| 350 RefPtrWillBeRawPtr<Element> root = Element::create(QualifiedName::null(), ta
ggedDocument.get()); | 350 RawPtr<Element> root = Element::create(QualifiedName::null(), taggedDocument
.get()); |
| 351 root->appendChild(taggedFragment.get()); | 351 root->appendChild(taggedFragment.get()); |
| 352 taggedDocument->appendChild(root); | 352 taggedDocument->appendChild(root); |
| 353 | 353 |
| 354 RefPtrWillBeRawPtr<Range> range = Range::create(*taggedDocument.get(), | 354 RawPtr<Range> range = Range::create(*taggedDocument.get(), |
| 355 positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(), | 355 positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(), |
| 356 positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent()); | 356 positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent()); |
| 357 | 357 |
| 358 Node* commonAncestor = range->commonAncestorContainer(); | 358 Node* commonAncestor = range->commonAncestorContainer(); |
| 359 HTMLElement* specialCommonAncestor = ancestorToRetainStructureAndAppearanceW
ithNoLayoutObject(commonAncestor); | 359 HTMLElement* specialCommonAncestor = ancestorToRetainStructureAndAppearanceW
ithNoLayoutObject(commonAncestor); |
| 360 | 360 |
| 361 // When there's a special common ancestor outside of the fragment, we must i
nclude it as well to | 361 // When there's a special common ancestor outside of the fragment, we must i
nclude it as well to |
| 362 // preserve the structure and appearance of the fragment. For example, if th
e fragment contains | 362 // preserve the structure and appearance of the fragment. For example, if th
e fragment contains |
| 363 // TD, we need to include the enclosing TABLE tag as well. | 363 // TD, we need to include the enclosing TABLE tag as well. |
| 364 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc
ument); | 364 RawPtr<DocumentFragment> fragment = DocumentFragment::create(document); |
| 365 if (specialCommonAncestor) | 365 if (specialCommonAncestor) |
| 366 fragment->appendChild(specialCommonAncestor); | 366 fragment->appendChild(specialCommonAncestor); |
| 367 else | 367 else |
| 368 fragment->parserTakeAllChildrenFrom(toContainerNode(*commonAncestor)); | 368 fragment->parserTakeAllChildrenFrom(toContainerNode(*commonAncestor)); |
| 369 | 369 |
| 370 trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get()
); | 370 trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get()
); |
| 371 | 371 |
| 372 return fragment; | 372 return fragment; |
| 373 } | 373 } |
| 374 | 374 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 399 size_t numEntries = tabList.size(); | 399 size_t numEntries = tabList.size(); |
| 400 for (size_t i = 0; i < numEntries; ++i) { | 400 for (size_t i = 0; i < numEntries; ++i) { |
| 401 const String& s = tabList[i]; | 401 const String& s = tabList[i]; |
| 402 | 402 |
| 403 // append the non-tab textual part | 403 // append the non-tab textual part |
| 404 if (!s.isEmpty()) { | 404 if (!s.isEmpty()) { |
| 405 if (!tabText.isEmpty()) { | 405 if (!tabText.isEmpty()) { |
| 406 paragraph->appendChild(createTabSpanElement(document, tabText.to
String())); | 406 paragraph->appendChild(createTabSpanElement(document, tabText.to
String())); |
| 407 tabText.clear(); | 407 tabText.clear(); |
| 408 } | 408 } |
| 409 RefPtrWillBeRawPtr<Text> textNode = document.createTextNode(stringWi
thRebalancedWhitespace(s, first, i + 1 == numEntries)); | 409 RawPtr<Text> textNode = document.createTextNode(stringWithRebalanced
Whitespace(s, first, i + 1 == numEntries)); |
| 410 paragraph->appendChild(textNode.release()); | 410 paragraph->appendChild(textNode.release()); |
| 411 } | 411 } |
| 412 | 412 |
| 413 // there is a tab after every entry, except the last entry | 413 // there is a tab after every entry, except the last entry |
| 414 // (if the last character is a tab, the list gets an extra empty entry) | 414 // (if the last character is a tab, the list gets an extra empty entry) |
| 415 if (i + 1 != numEntries) | 415 if (i + 1 != numEntries) |
| 416 tabText.append('\t'); | 416 tabText.append('\t'); |
| 417 else if (!tabText.isEmpty()) | 417 else if (!tabText.isEmpty()) |
| 418 paragraph->appendChild(createTabSpanElement(document, tabText.toStri
ng())); | 418 paragraph->appendChild(createTabSpanElement(document, tabText.toStri
ng())); |
| 419 | 419 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 445 } | 445 } |
| 446 | 446 |
| 447 if (Node* node = range.startPosition().anchorNode()) { | 447 if (Node* node = range.startPosition().anchorNode()) { |
| 448 if (LayoutObject* layoutObject = node->layoutObject()) | 448 if (LayoutObject* layoutObject = node->layoutObject()) |
| 449 return layoutObject->style()->preserveNewline(); | 449 return layoutObject->style()->preserveNewline(); |
| 450 } | 450 } |
| 451 | 451 |
| 452 return false; | 452 return false; |
| 453 } | 453 } |
| 454 | 454 |
| 455 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentFromText(const EphemeralR
ange& context, const String& text) | 455 RawPtr<DocumentFragment> createFragmentFromText(const EphemeralRange& context, c
onst String& text) |
| 456 { | 456 { |
| 457 if (context.isNull()) | 457 if (context.isNull()) |
| 458 return nullptr; | 458 return nullptr; |
| 459 | 459 |
| 460 Document& document = context.document(); | 460 Document& document = context.document(); |
| 461 RefPtrWillBeRawPtr<DocumentFragment> fragment = document.createDocumentFragm
ent(); | 461 RawPtr<DocumentFragment> fragment = document.createDocumentFragment(); |
| 462 | 462 |
| 463 if (text.isEmpty()) | 463 if (text.isEmpty()) |
| 464 return fragment.release(); | 464 return fragment.release(); |
| 465 | 465 |
| 466 String string = text; | 466 String string = text; |
| 467 string.replace("\r\n", "\n"); | 467 string.replace("\r\n", "\n"); |
| 468 string.replace('\r', '\n'); | 468 string.replace('\r', '\n'); |
| 469 | 469 |
| 470 if (!isRichlyEditablePosition(context.startPosition()) || shouldPreserveNewl
ine(context)) { | 470 if (!isRichlyEditablePosition(context.startPosition()) || shouldPreserveNewl
ine(context)) { |
| 471 fragment->appendChild(document.createTextNode(string)); | 471 fragment->appendChild(document.createTextNode(string)); |
| 472 if (string.endsWith('\n')) { | 472 if (string.endsWith('\n')) { |
| 473 RefPtrWillBeRawPtr<HTMLBRElement> element = HTMLBRElement::create(do
cument); | 473 RawPtr<HTMLBRElement> element = HTMLBRElement::create(document); |
| 474 element->setAttribute(classAttr, AppleInterchangeNewline); | 474 element->setAttribute(classAttr, AppleInterchangeNewline); |
| 475 fragment->appendChild(element.release()); | 475 fragment->appendChild(element.release()); |
| 476 } | 476 } |
| 477 return fragment.release(); | 477 return fragment.release(); |
| 478 } | 478 } |
| 479 | 479 |
| 480 // A string with no newlines gets added inline, rather than being put into a
paragraph. | 480 // A string with no newlines gets added inline, rather than being put into a
paragraph. |
| 481 if (string.find('\n') == kNotFound) { | 481 if (string.find('\n') == kNotFound) { |
| 482 fillContainerFromString(fragment.get(), string); | 482 fillContainerFromString(fragment.get(), string); |
| 483 return fragment.release(); | 483 return fragment.release(); |
| 484 } | 484 } |
| 485 | 485 |
| 486 // Break string into paragraphs. Extra line breaks turn into empty paragraph
s. | 486 // Break string into paragraphs. Extra line breaks turn into empty paragraph
s. |
| 487 Element* block = enclosingBlock(context.startPosition().nodeAsRangeFirstNode
()); | 487 Element* block = enclosingBlock(context.startPosition().nodeAsRangeFirstNode
()); |
| 488 bool useClonesOfEnclosingBlock = block | 488 bool useClonesOfEnclosingBlock = block |
| 489 && !isHTMLBodyElement(*block) | 489 && !isHTMLBodyElement(*block) |
| 490 && !isHTMLHtmlElement(*block) | 490 && !isHTMLHtmlElement(*block) |
| 491 && block != rootEditableElementOf(context.startPosition()); | 491 && block != rootEditableElementOf(context.startPosition()); |
| 492 | 492 |
| 493 Vector<String> list; | 493 Vector<String> list; |
| 494 string.split('\n', true, list); // true gets us empty strings in the list | 494 string.split('\n', true, list); // true gets us empty strings in the list |
| 495 size_t numLines = list.size(); | 495 size_t numLines = list.size(); |
| 496 for (size_t i = 0; i < numLines; ++i) { | 496 for (size_t i = 0; i < numLines; ++i) { |
| 497 const String& s = list[i]; | 497 const String& s = list[i]; |
| 498 | 498 |
| 499 RefPtrWillBeRawPtr<Element> element = nullptr; | 499 RawPtr<Element> element = nullptr; |
| 500 if (s.isEmpty() && i + 1 == numLines) { | 500 if (s.isEmpty() && i + 1 == numLines) { |
| 501 // For last line, use the "magic BR" rather than a P. | 501 // For last line, use the "magic BR" rather than a P. |
| 502 element = HTMLBRElement::create(document); | 502 element = HTMLBRElement::create(document); |
| 503 element->setAttribute(classAttr, AppleInterchangeNewline); | 503 element->setAttribute(classAttr, AppleInterchangeNewline); |
| 504 } else { | 504 } else { |
| 505 if (useClonesOfEnclosingBlock) | 505 if (useClonesOfEnclosingBlock) |
| 506 element = block->cloneElementWithoutChildren(); | 506 element = block->cloneElementWithoutChildren(); |
| 507 else | 507 else |
| 508 element = createDefaultParagraphElement(document); | 508 element = createDefaultParagraphElement(document); |
| 509 fillContainerFromString(element.get(), s); | 509 fillContainerFromString(element.get(), s); |
| 510 } | 510 } |
| 511 fragment->appendChild(element.release()); | 511 fragment->appendChild(element.release()); |
| 512 } | 512 } |
| 513 return fragment.release(); | 513 return fragment.release(); |
| 514 } | 514 } |
| 515 | 515 |
| 516 String urlToMarkup(const KURL& url, const String& title) | 516 String urlToMarkup(const KURL& url, const String& title) |
| 517 { | 517 { |
| 518 StringBuilder markup; | 518 StringBuilder markup; |
| 519 markup.appendLiteral("<a href=\""); | 519 markup.appendLiteral("<a href=\""); |
| 520 markup.append(url.getString()); | 520 markup.append(url.getString()); |
| 521 markup.appendLiteral("\">"); | 521 markup.appendLiteral("\">"); |
| 522 MarkupFormatter::appendCharactersReplacingEntities(markup, title, 0, title.l
ength(), EntityMaskInPCDATA); | 522 MarkupFormatter::appendCharactersReplacingEntities(markup, title, 0, title.l
ength(), EntityMaskInPCDATA); |
| 523 markup.appendLiteral("</a>"); | 523 markup.appendLiteral("</a>"); |
| 524 return markup.toString(); | 524 return markup.toString(); |
| 525 } | 525 } |
| 526 | 526 |
| 527 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentForInnerOuterHTML(const S
tring& markup, Element* contextElement, ParserContentPolicy parserContentPolicy,
const char* method, ExceptionState& exceptionState) | 527 RawPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, E
lement* contextElement, ParserContentPolicy parserContentPolicy, const char* met
hod, ExceptionState& exceptionState) |
| 528 { | 528 { |
| 529 ASSERT(contextElement); | 529 ASSERT(contextElement); |
| 530 Document& document = isHTMLTemplateElement(*contextElement) ? contextElement
->document().ensureTemplateDocument() : contextElement->document(); | 530 Document& document = isHTMLTemplateElement(*contextElement) ? contextElement
->document().ensureTemplateDocument() : contextElement->document(); |
| 531 RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(doc
ument); | 531 RawPtr<DocumentFragment> fragment = DocumentFragment::create(document); |
| 532 | 532 |
| 533 if (document.isHTMLDocument()) { | 533 if (document.isHTMLDocument()) { |
| 534 fragment->parseHTML(markup, contextElement, parserContentPolicy); | 534 fragment->parseHTML(markup, contextElement, parserContentPolicy); |
| 535 return fragment; | 535 return fragment; |
| 536 } | 536 } |
| 537 | 537 |
| 538 bool wasValid = fragment->parseXML(markup, contextElement, parserContentPoli
cy); | 538 bool wasValid = fragment->parseXML(markup, contextElement, parserContentPoli
cy); |
| 539 if (!wasValid) { | 539 if (!wasValid) { |
| 540 exceptionState.throwDOMException(SyntaxError, "The provided markup is in
valid XML, and therefore cannot be inserted into an XML document."); | 540 exceptionState.throwDOMException(SyntaxError, "The provided markup is in
valid XML, and therefore cannot be inserted into an XML document."); |
| 541 return nullptr; | 541 return nullptr; |
| 542 } | 542 } |
| 543 return fragment.release(); | 543 return fragment.release(); |
| 544 } | 544 } |
| 545 | 545 |
| 546 PassRefPtrWillBeRawPtr<DocumentFragment> createFragmentForTransformToFragment(co
nst String& sourceString, const String& sourceMIMEType, Document& outputDoc) | 546 RawPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sour
ceString, const String& sourceMIMEType, Document& outputDoc) |
| 547 { | 547 { |
| 548 RefPtrWillBeRawPtr<DocumentFragment> fragment = outputDoc.createDocumentFrag
ment(); | 548 RawPtr<DocumentFragment> fragment = outputDoc.createDocumentFragment(); |
| 549 | 549 |
| 550 if (sourceMIMEType == "text/html") { | 550 if (sourceMIMEType == "text/html") { |
| 551 // As far as I can tell, there isn't a spec for how transformToFragment
is supposed to work. | 551 // As far as I can tell, there isn't a spec for how transformToFragment
is supposed to work. |
| 552 // Based on the documentation I can find, it looks like we want to start
parsing the fragment in the InBody insertion mode. | 552 // Based on the documentation I can find, it looks like we want to start
parsing the fragment in the InBody insertion mode. |
| 553 // Unfortunately, that's an implementation detail of the parser. | 553 // Unfortunately, that's an implementation detail of the parser. |
| 554 // We achieve that effect here by passing in a fake body element as cont
ext for the fragment. | 554 // We achieve that effect here by passing in a fake body element as cont
ext for the fragment. |
| 555 RefPtrWillBeRawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(o
utputDoc); | 555 RawPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc); |
| 556 fragment->parseHTML(sourceString, fakeBody.get()); | 556 fragment->parseHTML(sourceString, fakeBody.get()); |
| 557 } else if (sourceMIMEType == "text/plain") { | 557 } else if (sourceMIMEType == "text/plain") { |
| 558 fragment->parserAppendChild(Text::create(outputDoc, sourceString)); | 558 fragment->parserAppendChild(Text::create(outputDoc, sourceString)); |
| 559 } else { | 559 } else { |
| 560 bool successfulParse = fragment->parseXML(sourceString, 0); | 560 bool successfulParse = fragment->parseXML(sourceString, 0); |
| 561 if (!successfulParse) | 561 if (!successfulParse) |
| 562 return nullptr; | 562 return nullptr; |
| 563 } | 563 } |
| 564 | 564 |
| 565 // FIXME: Do we need to mess with URLs here? | 565 // FIXME: Do we need to mess with URLs here? |
| 566 | 566 |
| 567 return fragment.release(); | 567 return fragment.release(); |
| 568 } | 568 } |
| 569 | 569 |
| 570 static inline void removeElementPreservingChildren(PassRefPtrWillBeRawPtr<Docume
ntFragment> fragment, HTMLElement* element) | 570 static inline void removeElementPreservingChildren(RawPtr<DocumentFragment> frag
ment, HTMLElement* element) |
| 571 { | 571 { |
| 572 RefPtrWillBeRawPtr<Node> nextChild = nullptr; | 572 RawPtr<Node> nextChild = nullptr; |
| 573 for (RefPtrWillBeRawPtr<Node> child = element->firstChild(); child; child =
nextChild) { | 573 for (RawPtr<Node> child = element->firstChild(); child; child = nextChild) { |
| 574 nextChild = child->nextSibling(); | 574 nextChild = child->nextSibling(); |
| 575 element->removeChild(child.get()); | 575 element->removeChild(child.get()); |
| 576 fragment->insertBefore(child, element); | 576 fragment->insertBefore(child, element); |
| 577 } | 577 } |
| 578 fragment->removeChild(element); | 578 fragment->removeChild(element); |
| 579 } | 579 } |
| 580 | 580 |
| 581 static inline bool isSupportedContainer(Element* element) | 581 static inline bool isSupportedContainer(Element* element) |
| 582 { | 582 { |
| 583 ASSERT(element); | 583 ASSERT(element); |
| 584 if (!element->isHTMLElement()) | 584 if (!element->isHTMLElement()) |
| 585 return true; | 585 return true; |
| 586 | 586 |
| 587 HTMLElement& htmlElement = toHTMLElement(*element); | 587 HTMLElement& htmlElement = toHTMLElement(*element); |
| 588 if (htmlElement.hasTagName(colTag) || htmlElement.hasTagName(colgroupTag) ||
htmlElement.hasTagName(framesetTag) | 588 if (htmlElement.hasTagName(colTag) || htmlElement.hasTagName(colgroupTag) ||
htmlElement.hasTagName(framesetTag) |
| 589 || htmlElement.hasTagName(headTag) || htmlElement.hasTagName(styleTag) |
| htmlElement.hasTagName(titleTag)) { | 589 || htmlElement.hasTagName(headTag) || htmlElement.hasTagName(styleTag) |
| htmlElement.hasTagName(titleTag)) { |
| 590 return false; | 590 return false; |
| 591 } | 591 } |
| 592 return !htmlElement.ieForbidsInsertHTML(); | 592 return !htmlElement.ieForbidsInsertHTML(); |
| 593 } | 593 } |
| 594 | 594 |
| 595 PassRefPtrWillBeRawPtr<DocumentFragment> createContextualFragment(const String&
markup, Element* element, ParserContentPolicy parserContentPolicy, ExceptionStat
e& exceptionState) | 595 RawPtr<DocumentFragment> createContextualFragment(const String& markup, Element*
element, ParserContentPolicy parserContentPolicy, ExceptionState& exceptionStat
e) |
| 596 { | 596 { |
| 597 ASSERT(element); | 597 ASSERT(element); |
| 598 if (!isSupportedContainer(element)) { | 598 if (!isSupportedContainer(element)) { |
| 599 exceptionState.throwDOMException(NotSupportedError, "The range's contain
er is '" + element->localName() + "', which is not supported."); | 599 exceptionState.throwDOMException(NotSupportedError, "The range's contain
er is '" + element->localName() + "', which is not supported."); |
| 600 return nullptr; | 600 return nullptr; |
| 601 } | 601 } |
| 602 | 602 |
| 603 RefPtrWillBeRawPtr<DocumentFragment> fragment = createFragmentForInnerOuterH
TML(markup, element, parserContentPolicy, "createContextualFragment", exceptionS
tate); | 603 RawPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup,
element, parserContentPolicy, "createContextualFragment", exceptionState); |
| 604 if (!fragment) | 604 if (!fragment) |
| 605 return nullptr; | 605 return nullptr; |
| 606 | 606 |
| 607 // We need to pop <html> and <body> elements and remove <head> to | 607 // We need to pop <html> and <body> elements and remove <head> to |
| 608 // accommodate folks passing complete HTML documents to make the | 608 // accommodate folks passing complete HTML documents to make the |
| 609 // child of an element. | 609 // child of an element. |
| 610 | 610 |
| 611 RefPtrWillBeRawPtr<Node> nextNode = nullptr; | 611 RawPtr<Node> nextNode = nullptr; |
| 612 for (RefPtrWillBeRawPtr<Node> node = fragment->firstChild(); node; node = ne
xtNode) { | 612 for (RawPtr<Node> node = fragment->firstChild(); node; node = nextNode) { |
| 613 nextNode = node->nextSibling(); | 613 nextNode = node->nextSibling(); |
| 614 if (isHTMLHtmlElement(*node) || isHTMLHeadElement(*node) || isHTMLBodyEl
ement(*node)) { | 614 if (isHTMLHtmlElement(*node) || isHTMLHeadElement(*node) || isHTMLBodyEl
ement(*node)) { |
| 615 HTMLElement* element = toHTMLElement(node); | 615 HTMLElement* element = toHTMLElement(node); |
| 616 if (Node* firstChild = element->firstChild()) | 616 if (Node* firstChild = element->firstChild()) |
| 617 nextNode = firstChild; | 617 nextNode = firstChild; |
| 618 removeElementPreservingChildren(fragment, element); | 618 removeElementPreservingChildren(fragment, element); |
| 619 } | 619 } |
| 620 } | 620 } |
| 621 return fragment.release(); | 621 return fragment.release(); |
| 622 } | 622 } |
| 623 | 623 |
| 624 void replaceChildrenWithFragment(ContainerNode* container, PassRefPtrWillBeRawPt
r<DocumentFragment> fragment, ExceptionState& exceptionState) | 624 void replaceChildrenWithFragment(ContainerNode* container, RawPtr<DocumentFragme
nt> fragment, ExceptionState& exceptionState) |
| 625 { | 625 { |
| 626 ASSERT(container); | 626 ASSERT(container); |
| 627 RefPtrWillBeRawPtr<ContainerNode> containerNode(container); | 627 RawPtr<ContainerNode> containerNode(container); |
| 628 | 628 |
| 629 ChildListMutationScope mutation(*containerNode); | 629 ChildListMutationScope mutation(*containerNode); |
| 630 | 630 |
| 631 if (!fragment->firstChild()) { | 631 if (!fragment->firstChild()) { |
| 632 containerNode->removeChildren(); | 632 containerNode->removeChildren(); |
| 633 return; | 633 return; |
| 634 } | 634 } |
| 635 | 635 |
| 636 // FIXME: This is wrong if containerNode->firstChild() has more than one ref
! | 636 // FIXME: This is wrong if containerNode->firstChild() has more than one ref
! |
| 637 if (containerNode->hasOneTextChild() && fragment->hasOneTextChild()) { | 637 if (containerNode->hasOneTextChild() && fragment->hasOneTextChild()) { |
| 638 toText(containerNode->firstChild())->setData(toText(fragment->firstChild
())->data()); | 638 toText(containerNode->firstChild())->setData(toText(fragment->firstChild
())->data()); |
| 639 return; | 639 return; |
| 640 } | 640 } |
| 641 | 641 |
| 642 // FIXME: No need to replace the child it is a text node and its contents ar
e already == text. | 642 // FIXME: No need to replace the child it is a text node and its contents ar
e already == text. |
| 643 if (containerNode->hasOneChild()) { | 643 if (containerNode->hasOneChild()) { |
| 644 containerNode->replaceChild(fragment, containerNode->firstChild(), excep
tionState); | 644 containerNode->replaceChild(fragment, containerNode->firstChild(), excep
tionState); |
| 645 return; | 645 return; |
| 646 } | 646 } |
| 647 | 647 |
| 648 containerNode->removeChildren(); | 648 containerNode->removeChildren(); |
| 649 containerNode->appendChild(fragment, exceptionState); | 649 containerNode->appendChild(fragment, exceptionState); |
| 650 } | 650 } |
| 651 | 651 |
| 652 void replaceChildrenWithText(ContainerNode* container, const String& text, Excep
tionState& exceptionState) | 652 void replaceChildrenWithText(ContainerNode* container, const String& text, Excep
tionState& exceptionState) |
| 653 { | 653 { |
| 654 ASSERT(container); | 654 ASSERT(container); |
| 655 RefPtrWillBeRawPtr<ContainerNode> containerNode(container); | 655 RawPtr<ContainerNode> containerNode(container); |
| 656 | 656 |
| 657 ChildListMutationScope mutation(*containerNode); | 657 ChildListMutationScope mutation(*containerNode); |
| 658 | 658 |
| 659 // FIXME: This is wrong if containerNode->firstChild() has more than one ref
! Example: | 659 // FIXME: This is wrong if containerNode->firstChild() has more than one ref
! Example: |
| 660 // <div>foo</div> | 660 // <div>foo</div> |
| 661 // <script> | 661 // <script> |
| 662 // var oldText = div.firstChild; | 662 // var oldText = div.firstChild; |
| 663 // console.log(oldText.data); // foo | 663 // console.log(oldText.data); // foo |
| 664 // div.innerText = "bar"; | 664 // div.innerText = "bar"; |
| 665 // console.log(oldText.data); // bar!?! | 665 // console.log(oldText.data); // bar!?! |
| 666 // </script> | 666 // </script> |
| 667 // I believe this is an intentional benchmark cheat from years ago. | 667 // I believe this is an intentional benchmark cheat from years ago. |
| 668 // We should re-visit if we actually want this still. | 668 // We should re-visit if we actually want this still. |
| 669 if (containerNode->hasOneTextChild()) { | 669 if (containerNode->hasOneTextChild()) { |
| 670 toText(containerNode->firstChild())->setData(text); | 670 toText(containerNode->firstChild())->setData(text); |
| 671 return; | 671 return; |
| 672 } | 672 } |
| 673 | 673 |
| 674 // NOTE: This method currently always creates a text node, even if that text
node will be empty. | 674 // NOTE: This method currently always creates a text node, even if that text
node will be empty. |
| 675 RefPtrWillBeRawPtr<Text> textNode = Text::create(containerNode->document(),
text); | 675 RawPtr<Text> textNode = Text::create(containerNode->document(), text); |
| 676 | 676 |
| 677 // FIXME: No need to replace the child it is a text node and its contents ar
e already == text. | 677 // FIXME: No need to replace the child it is a text node and its contents ar
e already == text. |
| 678 if (containerNode->hasOneChild()) { | 678 if (containerNode->hasOneChild()) { |
| 679 containerNode->replaceChild(textNode.release(), containerNode->firstChil
d(), exceptionState); | 679 containerNode->replaceChild(textNode.release(), containerNode->firstChil
d(), exceptionState); |
| 680 return; | 680 return; |
| 681 } | 681 } |
| 682 | 682 |
| 683 containerNode->removeChildren(); | 683 containerNode->removeChildren(); |
| 684 containerNode->appendChild(textNode.release(), exceptionState); | 684 containerNode->appendChild(textNode.release(), exceptionState); |
| 685 } | 685 } |
| 686 | 686 |
| 687 void mergeWithNextTextNode(Text* textNode, ExceptionState& exceptionState) | 687 void mergeWithNextTextNode(Text* textNode, ExceptionState& exceptionState) |
| 688 { | 688 { |
| 689 ASSERT(textNode); | 689 ASSERT(textNode); |
| 690 Node* next = textNode->nextSibling(); | 690 Node* next = textNode->nextSibling(); |
| 691 if (!next || !next->isTextNode()) | 691 if (!next || !next->isTextNode()) |
| 692 return; | 692 return; |
| 693 | 693 |
| 694 RefPtrWillBeRawPtr<Text> textNext = toText(next); | 694 RawPtr<Text> textNext = toText(next); |
| 695 textNode->appendData(textNext->data()); | 695 textNode->appendData(textNext->data()); |
| 696 if (textNext->parentNode()) // Might have been removed by mutation event. | 696 if (textNext->parentNode()) // Might have been removed by mutation event. |
| 697 textNext->remove(exceptionState); | 697 textNext->remove(exceptionState); |
| 698 } | 698 } |
| 699 | 699 |
| 700 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingStrategy>; | 700 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingStrategy>; |
| 701 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingInFlatTreeStrat
egy>; | 701 template class CORE_TEMPLATE_EXPORT CreateMarkupAlgorithm<EditingInFlatTreeStrat
egy>; |
| 702 | 702 |
| 703 } // namespace blink | 703 } // namespace blink |
| OLD | NEW |