1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
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/markup.h"
31
32#include "CSSPropertyNames.h"
33#include "CSSValueKeywords.h"
34#include "HTMLNames.h"
35#include "bindings/v8/ExceptionState.h"
36#include "core/css/CSSPrimitiveValue.h"
37#include "core/css/CSSValue.h"
38#include "core/css/StylePropertySet.h"
39#include "core/dom/CDATASection.h"
40#include "core/dom/ChildListMutationScope.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/Editor.h"
48#include "core/editing/MarkupAccumulator.h"
49#include "core/editing/TextIterator.h"
50#include "core/editing/VisibleSelection.h"
51#include "core/editing/VisibleUnits.h"
52#include "core/editing/htmlediting.h"
53#include "core/html/HTMLBodyElement.h"
54#include "core/html/HTMLElement.h"
55#include "core/html/HTMLHtmlElement.h"
56#include "core/html/HTMLTableElement.h"
57#include "core/html/HTMLTextFormControlElement.h"
58#include "core/frame/Frame.h"
59#include "core/rendering/RenderObject.h"
60#include "platform/weborigin/KURL.h"
61#include "wtf/StdLibExtras.h"
62#include "wtf/text/StringBuilder.h"
63
64using namespace std;
65
66namespace WebCore {
67
68using namespace HTMLNames;
69
70static bool propertyMissingOrEqualToNone(StylePropertySet*, CSSPropertyID);
71
72class AttributeChange {
73public:
74    AttributeChange()
75        : m_name(nullAtom, nullAtom, nullAtom)
76    {
77    }
78
79    AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value)
80        : m_element(element), m_name(name), m_value(value)
81    {
82    }
83
84    void apply()
85    {
86        m_element->setAttribute(m_name, AtomicString(m_value));
87    }
88
89private:
90    RefPtr<Element> m_element;
91    QualifiedName m_name;
92    String m_value;
93};
94
95static void completeURLs(DocumentFragment& fragment, const String& baseURL)
96{
97    Vector<AttributeChange> changes;
98
99    KURL parsedBaseURL(ParsedURLString, baseURL);
100
101    for (Element* element = ElementTraversal::firstWithin(fragment); element; element = ElementTraversal::next(*element, &fragment)) {
102        if (!element->hasAttributes())
103            continue;
104        unsigned length = element->attributeCount();
105        for (unsigned i = 0; i < length; i++) {
106            const Attribute* attribute = element->attributeItem(i);
107            if (element->isURLAttribute(*attribute) && !attribute->value().isEmpty())
108                changes.append(AttributeChange(element, attribute->name(), KURL(parsedBaseURL, attribute->value()).string()));
109        }
110    }
111
112    size_t numChanges = changes.size();
113    for (size_t i = 0; i < numChanges; ++i)
114        changes[i].apply();
115}
116
117class StyledMarkupAccumulator : public MarkupAccumulator {
118public:
119    enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
120
121    StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, Node* highestNodeToBeSerialized = 0);
122    Node* serializeNodes(Node* startNode, Node* pastEnd);
123    virtual void appendString(const String& s) { return MarkupAccumulator::appendString(s); }
124    void wrapWithNode(Node*, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode);
125    void wrapWithStyleNode(StylePropertySet*, Document*, bool isBlock = false);
126    String takeResults();
127
128private:
129    void appendStyleNodeOpenTag(StringBuilder&, StylePropertySet*, Document*, bool isBlock = false);
130    const String& styleNodeCloseTag(bool isBlock = false);
131    virtual void appendText(StringBuilder& out, Text*);
132    String renderedText(const Node*, const Range*);
133    String stringValueForRange(const Node*, const Range*);
134    void appendElement(StringBuilder& out, Element*, bool addDisplayInline, RangeFullySelectsNode);
135    void appendElement(StringBuilder& out, Element* element, Namespaces*) { appendElement(out, element, false, DoesFullySelectNode); }
136
137    enum NodeTraversalMode { EmitString, DoNotEmitString };
138    Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode);
139
140    bool shouldAnnotate() { return m_shouldAnnotate == AnnotateForInterchange; }
141    bool shouldApplyWrappingStyle(Node* node) const
142    {
143        return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node->parentNode()
144            && m_wrappingStyle && m_wrappingStyle->style();
145    }
146
147    Vector<String> m_reversedPrecedingMarkup;
148    const EAnnotateForInterchange m_shouldAnnotate;
149    Node* m_highestNodeToBeSerialized;
150    RefPtr<EditingStyle> m_wrappingStyle;
151};
152
153inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate,
154    const Range* range, Node* highestNodeToBeSerialized)
155    : MarkupAccumulator(nodes, shouldResolveURLs, range)
156    , m_shouldAnnotate(shouldAnnotate)
157    , m_highestNodeToBeSerialized(highestNodeToBeSerialized)
158{
159}
160
161void StyledMarkupAccumulator::wrapWithNode(Node* node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
162{
163    StringBuilder markup;
164    if (node->isElementNode())
165        appendElement(markup, toElement(node), convertBlocksToInlines && isBlock(const_cast<Node*>(node)), rangeFullySelectsNode);
166    else
167        appendStartMarkup(markup, node, 0);
168    m_reversedPrecedingMarkup.append(markup.toString());
169    appendEndTag(node);
170    if (m_nodes)
171        m_nodes->append(node);
172}
173
174void StyledMarkupAccumulator::wrapWithStyleNode(StylePropertySet* style, Document* document, bool isBlock)
175{
176    StringBuilder openTag;
177    appendStyleNodeOpenTag(openTag, style, document, isBlock);
178    m_reversedPrecedingMarkup.append(openTag.toString());
179    appendString(styleNodeCloseTag(isBlock));
180}
181
182void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StylePropertySet* style, Document* document, bool isBlock)
183{
184    // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect
185    ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect));
186    if (isBlock)
187        out.appendLiteral("<div style=\"");
188    else
189        out.appendLiteral("<span style=\"");
190    appendAttributeValue(out, style->asText(), document->isHTMLDocument());
191    out.append('\"');
192    out.append('>');
193}
194
195const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
196{
197    DEFINE_STATIC_LOCAL(const String, divClose, ("</div>"));
198    DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>"));
199    return isBlock ? divClose : styleSpanClose;
200}
201
202String StyledMarkupAccumulator::takeResults()
203{
204    StringBuilder result;
205    result.reserveCapacity(totalLength(m_reversedPrecedingMarkup) + length());
206
207    for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i)
208        result.append(m_reversedPrecedingMarkup[i - 1]);
209
210    concatenateMarkup(result);
211
212    // We remove '\0' characters because they are not visibly rendered to the user.
213    return result.toString().replace(0, "");
214}
215
216void StyledMarkupAccumulator::appendText(StringBuilder& out, Text* text)
217{
218    const bool parentIsTextarea = text->parentElement() && text->parentElement()->tagQName() == textareaTag;
219    const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
220    if (wrappingSpan) {
221        RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
222        // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
223        // Make sure spans are inline style in paste side e.g. span { display: block }.
224        wrappingStyle->forceInline();
225        // FIXME: Should this be included in forceInline?
226        wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);
227
228        appendStyleNodeOpenTag(out, wrappingStyle->style(), &text->document());
229    }
230
231    if (!shouldAnnotate() || parentIsTextarea)
232        MarkupAccumulator::appendText(out, text);
233    else {
234        const bool useRenderedText = !enclosingNodeWithTag(firstPositionInNode(text), selectTag);
235        String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
236        StringBuilder buffer;
237        appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA);
238        out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), text));
239    }
240
241    if (wrappingSpan)
242        out.append(styleNodeCloseTag());
243}
244
245String StyledMarkupAccumulator::renderedText(const Node* node, const Range* range)
246{
247    if (!node->isTextNode())
248        return String();
249
250    const Text* textNode = toText(node);
251    unsigned startOffset = 0;
252    unsigned endOffset = textNode->length();
253
254    if (range && node == range->startContainer())
255        startOffset = range->startOffset();
256    if (range && node == range->endContainer())
257        endOffset = range->endOffset();
258
259    Position start = createLegacyEditingPosition(const_cast<Node*>(node), startOffset);
260    Position end = createLegacyEditingPosition(const_cast<Node*>(node), endOffset);
261    return plainText(Range::create(node->document(), start, end).get());
262}
263
264String StyledMarkupAccumulator::stringValueForRange(const Node* node, const Range* range)
265{
266    if (!range)
267        return node->nodeValue();
268
269    String str = node->nodeValue();
270    if (node == range->endContainer())
271        str.truncate(range->endOffset());
272    if (node == range->startContainer())
273        str.remove(0, range->startOffset());
274    return str;
275}
276
277void StyledMarkupAccumulator::appendElement(StringBuilder& out, Element* element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
278{
279    const bool documentIsHTML = element->document().isHTMLDocument();
280    appendOpenTag(out, element, 0);
281
282    const unsigned length = element->hasAttributes() ? element->attributeCount() : 0;
283    const bool shouldAnnotateOrForceInline = element->isHTMLElement() && (shouldAnnotate() || addDisplayInline);
284    const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
285    for (unsigned i = 0; i < length; ++i) {
286        const Attribute* attribute = element->attributeItem(i);
287        // We'll handle the style attribute separately, below.
288        if (attribute->name() == styleAttr && shouldOverrideStyleAttr)
289            continue;
290        appendAttribute(out, element, *attribute, 0);
291    }
292
293    if (shouldOverrideStyleAttr) {
294        RefPtr<EditingStyle> newInlineStyle;
295
296        if (shouldApplyWrappingStyle(element)) {
297            newInlineStyle = m_wrappingStyle->copy();
298            newInlineStyle->removePropertiesInElementDefaultStyle(element);
299            newInlineStyle->removeStyleConflictingWithStyleOfNode(element);
300        } else
301            newInlineStyle = EditingStyle::create();
302
303        if (element->isStyledElement() && element->inlineStyle())
304            newInlineStyle->overrideWithStyle(element->inlineStyle());
305
306        if (shouldAnnotateOrForceInline) {
307            if (shouldAnnotate())
308                newInlineStyle->mergeStyleFromRulesForSerialization(toHTMLElement(element));
309
310            if (addDisplayInline)
311                newInlineStyle->forceInline();
312
313            // If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it
314            // only the ones that affect it and the nodes within it.
315            if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style())
316                newInlineStyle->style()->removeProperty(CSSPropertyFloat);
317        }
318
319        if (!newInlineStyle->isEmpty()) {
320            out.appendLiteral(" style=\"");
321            appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML);
322            out.append('\"');
323        }
324    }
325
326    appendCloseTag(out, element);
327}
328
329Node* StyledMarkupAccumulator::serializeNodes(Node* startNode, Node* pastEnd)
330{
331    if (!m_highestNodeToBeSerialized) {
332        Node* lastClosed = traverseNodesForSerialization(startNode, pastEnd, DoNotEmitString);
333        m_highestNodeToBeSerialized = lastClosed;
334    }
335
336    if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode())
337        m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(m_highestNodeToBeSerialized->parentNode(), shouldAnnotate());
338
339    return traverseNodesForSerialization(startNode, pastEnd, EmitString);
340}
341
342Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode)
343{
344    const bool shouldEmit = traversalMode == EmitString;
345    Vector<Node*> ancestorsToClose;
346    Node* next;
347    Node* lastClosed = 0;
348    for (Node* n = startNode; n != pastEnd; n = next) {
349        // According to <rdar://problem/5730668>, it is possible for n to blow
350        // past pastEnd and become null here. This shouldn't be possible.
351        // This null check will prevent crashes (but create too much markup)
352        // and the ASSERT will hopefully lead us to understanding the problem.
353        ASSERT(n);
354        if (!n)
355            break;
356
357        next = NodeTraversal::next(*n);
358        bool openedTag = false;
359
360        if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
361            // Don't write out empty block containers that aren't fully selected.
362            continue;
363
364        if (!n->renderer() && !enclosingNodeWithTag(firstPositionInOrBeforeNode(n), selectTag)) {
365            next = NodeTraversal::nextSkippingChildren(*n);
366            // Don't skip over pastEnd.
367            if (pastEnd && pastEnd->isDescendantOf(n))
368                next = pastEnd;
369        } else {
370            // Add the node to the markup if we're not skipping the descendants
371            if (shouldEmit)
372                appendStartTag(n);
373
374            // If node has no children, close the tag now.
375            if (!n->childNodeCount()) {
376                if (shouldEmit)
377                    appendEndTag(n);
378                lastClosed = n;
379            } else {
380                openedTag = true;
381                ancestorsToClose.append(n);
382            }
383        }
384
385        // If we didn't insert open tag and there's no more siblings or we're at the end of the traversal, take care of ancestors.
386        // FIXME: What happens if we just inserted open tag and reached the end?
387        if (!openedTag && (!n->nextSibling() || next == pastEnd)) {
388            // Close up the ancestors.
389            while (!ancestorsToClose.isEmpty()) {
390                Node* ancestor = ancestorsToClose.last();
391                if (next != pastEnd && next->isDescendantOf(ancestor))
392                    break;
393                // Not at the end of the range, close ancestors up to sibling of next node.
394                if (shouldEmit)
395                    appendEndTag(ancestor);
396                lastClosed = ancestor;
397                ancestorsToClose.removeLast();
398            }
399
400            // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
401            ContainerNode* nextParent = next ? next->parentNode() : 0;
402            if (next != pastEnd && n != nextParent) {
403                Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
404                for (ContainerNode* parent = lastAncestorClosedOrSelf->parentNode(); parent && parent != nextParent; parent = parent->parentNode()) {
405                    // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
406                    if (!parent->renderer())
407                        continue;
408                    // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
409                    ASSERT(startNode->isDescendantOf(parent));
410                    if (shouldEmit)
411                        wrapWithNode(parent);
412                    lastClosed = parent;
413                }
414            }
415        }
416    }
417
418    return lastClosed;
419}
420
421static bool isHTMLBlockElement(const Node* node)
422{
423    return node->hasTagName(tdTag)
424        || node->hasTagName(thTag)
425        || isNonTableCellHTMLBlockElement(node);
426}
427
428static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
429{
430    if (!commonAncestorBlock)
431        return 0;
432
433    if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
434        ContainerNode* table = commonAncestorBlock->parentNode();
435        while (table && !isHTMLTableElement(table))
436            table = table->parentNode();
437
438        return table;
439    }
440
441    if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
442        return commonAncestorBlock;
443
444    return 0;
445}
446
447static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
448{
449    return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor));
450}
451
452static inline Node* ancestorToRetainStructureAndAppearanceWithNoRenderer(Node* commonAncestor)
453{
454    Node* commonAncestorBlock = enclosingNodeOfType(firstPositionInOrBeforeNode(commonAncestor), isHTMLBlockElement);
455    return ancestorToRetainStructureAndAppearanceForBlock(commonAncestorBlock);
456}
457
458static bool propertyMissingOrEqualToNone(StylePropertySet* style, CSSPropertyID propertyID)
459{
460    if (!style)
461        return false;
462    RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
463    if (!value)
464        return true;
465    if (!value->isPrimitiveValue())
466        return false;
467    return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone;
468}
469
470static bool needInterchangeNewlineAfter(const VisiblePosition& v)
471{
472    VisiblePosition next = v.next();
473    Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode();
474    Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode();
475    // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it.
476    return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
477}
478
479static PassRefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node)
480{
481    if (!node->isHTMLElement())
482        return 0;
483
484    // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle
485    // the non-const-ness of styleFromMatchedRulesForElement.
486    HTMLElement* element = const_cast<HTMLElement*>(toHTMLElement(node));
487    RefPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle());
488    style->mergeStyleFromRules(element);
489    return style.release();
490}
491
492static bool isElementPresentational(const Node* node)
493{
494    return node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag)
495        || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag);
496}
497
498static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterchange shouldAnnotate, Node* constrainingAncestor)
499{
500    Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
501    ASSERT(commonAncestor);
502    Node* specialCommonAncestor = 0;
503    if (shouldAnnotate == AnnotateForInterchange) {
504        // Include ancestors that aren't completely inside the range but are required to retain
505        // the structure and appearance of the copied markup.
506        specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor);
507
508        if (Node* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) {
509            if (WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) {
510                specialCommonAncestor = parentListNode->parentNode();
511                while (specialCommonAncestor && !isListElement(specialCommonAncestor))
512                    specialCommonAncestor = specialCommonAncestor->parentNode();
513            }
514        }
515
516        // Retain the Mail quote level by including all ancestor mail block quotes.
517        if (Node* highestMailBlockquote = highestEnclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isMailBlockquote, CanCrossEditingBoundary))
518            specialCommonAncestor = highestMailBlockquote;
519    }
520
521    Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
522    if (checkAncestor->renderer()) {
523        Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, constrainingAncestor);
524        if (newSpecialCommonAncestor)
525            specialCommonAncestor = newSpecialCommonAncestor;
526    }
527
528    // If a single tab is selected, commonAncestor will be a text node inside a tab span.
529    // If two or more tabs are selected, commonAncestor will be the tab span.
530    // In either case, if there is a specialCommonAncestor already, it will necessarily be above
531    // any tab span that needs to be included.
532    if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor))
533        specialCommonAncestor = commonAncestor->parentNode();
534    if (!specialCommonAncestor && isTabSpanNode(commonAncestor))
535        specialCommonAncestor = commonAncestor;
536
537    if (Node *enclosingAnchor = enclosingNodeWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))
538        specialCommonAncestor = enclosingAnchor;
539
540    return specialCommonAncestor;
541}
542
543// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
544// FIXME: At least, annotation and style info should probably not be included in range.markupString()
545static String createMarkupInternal(Document& document, const Range* range, const Range* updatedRange, Vector<Node*>* nodes,
546    EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor)
547{
548    ASSERT(range);
549    ASSERT(updatedRange);
550    DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">"));
551
552    bool collapsed = updatedRange->collapsed(ASSERT_NO_EXCEPTION);
553    if (collapsed)
554        return emptyString();
555    Node* commonAncestor = updatedRange->commonAncestorContainer(ASSERT_NO_EXCEPTION);
556    if (!commonAncestor)
557        return emptyString();
558
559    document.updateLayoutIgnorePendingStylesheets();
560
561    Node* body = enclosingNodeWithTag(firstPositionInNode(commonAncestor), bodyTag);
562    Node* fullySelectedRoot = 0;
563    // FIXME: Do this for all fully selected blocks, not just the body.
564    if (body && areRangesEqual(VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange().get(), range))
565        fullySelectedRoot = body;
566    Node* specialCommonAncestor = highestAncestorToWrapMarkup(updatedRange, shouldAnnotate, constrainingAncestor);
567    StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, updatedRange, specialCommonAncestor);
568    Node* pastEnd = updatedRange->pastLastNode();
569
570    Node* startNode = updatedRange->firstNode();
571    VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY);
572    VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY);
573    if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) {
574        if (visibleStart == visibleEnd.previous())
575            return interchangeNewlineString;
576
577        accumulator.appendString(interchangeNewlineString);
578        startNode = visibleStart.next().deepEquivalent().deprecatedNode();
579
580        if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0)
581            return interchangeNewlineString;
582    }
583
584    Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd);
585
586    if (specialCommonAncestor && lastClosed) {
587        // Also include all of the ancestors of lastClosed up to this special ancestor.
588        for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
589            if (ancestor == fullySelectedRoot && !convertBlocksToInlines) {
590                RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot);
591
592                // Bring the background attribute over, but not as an attribute because a background attribute on a div
593                // appears to have no effect.
594                if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage))
595                    && toElement(fullySelectedRoot)->hasAttribute(backgroundAttr))
596                    fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + toElement(fullySelectedRoot)->getAttribute(backgroundAttr) + "')");
597
598                if (fullySelectedRootStyle->style()) {
599                    // Reset the CSS properties to avoid an assertion error in addStyleMarkup().
600                    // This assertion is caused at least when we select all text of a <body> element whose
601                    // 'text-decoration' property is "inherit", and copy it.
602                    if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration))
603                        fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone);
604                    if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect))
605                        fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone);
606                    accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), &document, true);
607                }
608            } else {
609                // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode
610                // so that styles that affect the exterior of the node are not included.
611                accumulator.wrapWithNode(ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode);
612            }
613            if (nodes)
614                nodes->append(ancestor);
615
616            lastClosed = ancestor;
617
618            if (ancestor == specialCommonAncestor)
619                break;
620        }
621    }
622
623    // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
624    if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous()))
625        accumulator.appendString(interchangeNewlineString);
626
627    return accumulator.takeResults();
628}
629
630String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor)
631{
632    if (!range)
633        return emptyString();
634
635    Document& document = range->ownerDocument();
636    const Range* updatedRange = range;
637
638    return createMarkupInternal(document, range, updatedRange, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs, constrainingAncestor);
639}
640
641PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
642{
643    // We use a fake body element here to trick the HTML parser to using the InBody insertion mode.
644    RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document);
645    RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
646
647    fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy);
648
649    if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL())
650        completeURLs(*fragment, baseURL);
651
652    return fragment.release();
653}
654
655static const char fragmentMarkerTag[] = "webkit-fragment-marker";
656
657static bool findNodesSurroundingContext(Document* document, RefPtr<Node>& nodeBeforeContext, RefPtr<Node>& nodeAfterContext)
658{
659    for (Node* node = document->firstChild(); node; node = NodeTraversal::next(*node)) {
660        if (node->nodeType() == Node::COMMENT_NODE && toCharacterData(node)->data() == fragmentMarkerTag) {
661            if (!nodeBeforeContext)
662                nodeBeforeContext = node;
663            else {
664                nodeAfterContext = node;
665                return true;
666            }
667        }
668    }
669    return false;
670}
671
672static void trimFragment(DocumentFragment* fragment, Node* nodeBeforeContext, Node* nodeAfterContext)
673{
674    RefPtr<Node> next;
675    for (RefPtr<Node> node = fragment->firstChild(); node; node = next) {
676        if (nodeBeforeContext->isDescendantOf(node.get())) {
677            next = NodeTraversal::next(*node);
678            continue;
679        }
680        next = NodeTraversal::nextSkippingChildren(*node);
681        ASSERT(!node->contains(nodeAfterContext));
682        node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
683        if (nodeBeforeContext == node)
684            break;
685    }
686
687    ASSERT(nodeAfterContext->parentNode());
688    for (RefPtr<Node> node = nodeAfterContext; node; node = next) {
689        next = NodeTraversal::nextSkippingChildren(*node);
690        node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
691    }
692}
693
694PassRefPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document& document, const String& markup, unsigned fragmentStart, unsigned fragmentEnd,
695    const String& baseURL, ParserContentPolicy parserContentPolicy)
696{
697    // FIXME: Need to handle the case where the markup already contains these markers.
698
699    StringBuilder taggedMarkup;
700    taggedMarkup.append(markup.left(fragmentStart));
701    MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
702    taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentStart));
703    MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
704    taggedMarkup.append(markup.substring(fragmentEnd));
705
706    RefPtr<DocumentFragment> taggedFragment = createFragmentFromMarkup(document, taggedMarkup.toString(), baseURL, parserContentPolicy);
707    RefPtr<Document> taggedDocument = Document::create();
708    taggedDocument->setContextFeatures(document.contextFeatures());
709
710    // FIXME: It's not clear what this code is trying to do. It puts nodes as direct children of a
711    // Document that are not normally allowed by using the parser machinery.
712    taggedDocument->parserTakeAllChildrenFrom(*taggedFragment);
713
714    RefPtr<Node> nodeBeforeContext;
715    RefPtr<Node> nodeAfterContext;
716    if (!findNodesSurroundingContext(taggedDocument.get(), nodeBeforeContext, nodeAfterContext))
717        return 0;
718
719    RefPtr<Range> range = Range::create(*taggedDocument.get(),
720        positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(),
721        positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent());
722
723    Node* commonAncestor = range->commonAncestorContainer(ASSERT_NO_EXCEPTION);
724    Node* specialCommonAncestor = ancestorToRetainStructureAndAppearanceWithNoRenderer(commonAncestor);
725
726    // When there's a special common ancestor outside of the fragment, we must include it as well to
727    // preserve the structure and appearance of the fragment. For example, if the fragment contains
728    // TD, we need to include the enclosing TABLE tag as well.
729    RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
730    if (specialCommonAncestor)
731        fragment->appendChild(specialCommonAncestor);
732    else
733        fragment->parserTakeAllChildrenFrom(toContainerNode(*commonAncestor));
734
735    trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get());
736
737    return fragment;
738}
739
740String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip)
741{
742    if (!node)
743        return "";
744
745    MarkupAccumulator accumulator(nodes, shouldResolveURLs);
746    return accumulator.serializeNodes(const_cast<Node*>(node), childrenOnly, tagNamesToSkip);
747}
748
749static void fillContainerFromString(ContainerNode* paragraph, const String& string)
750{
751    Document& document = paragraph->document();
752
753    if (string.isEmpty()) {
754        paragraph->appendChild(createBlockPlaceholderElement(document));
755        return;
756    }
757
758    ASSERT(string.find('\n') == kNotFound);
759
760    Vector<String> tabList;
761    string.split('\t', true, tabList);
762    String tabText = emptyString();
763    bool first = true;
764    size_t numEntries = tabList.size();
765    for (size_t i = 0; i < numEntries; ++i) {
766        const String& s = tabList[i];
767
768        // append the non-tab textual part
769        if (!s.isEmpty()) {
770            if (!tabText.isEmpty()) {
771                paragraph->appendChild(createTabSpanElement(document, tabText));
772                tabText = emptyString();
773            }
774            RefPtr<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
775            paragraph->appendChild(textNode.release());
776        }
777
778        // there is a tab after every entry, except the last entry
779        // (if the last character is a tab, the list gets an extra empty entry)
780        if (i + 1 != numEntries)
781            tabText.append('\t');
782        else if (!tabText.isEmpty())
783            paragraph->appendChild(createTabSpanElement(document, tabText));
784
785        first = false;
786    }
787}
788
789bool isPlainTextMarkup(Node *node)
790{
791    if (!node->isElementNode() || !node->hasTagName(divTag) || toElement(node)->hasAttributes())
792        return false;
793
794    if (node->childNodeCount() == 1 && (node->firstChild()->isTextNode() || (node->firstChild()->firstChild())))
795        return true;
796
797    return (node->childNodeCount() == 2 && isTabSpanTextNode(node->firstChild()->firstChild()) && node->firstChild()->nextSibling()->isTextNode());
798}
799
800static bool shouldPreserveNewline(const Range& range)
801{
802    if (Node* node = range.firstNode()) {
803        if (RenderObject* renderer = node->renderer())
804            return renderer->style()->preserveNewline();
805    }
806
807    if (Node* node = range.startPosition().anchorNode()) {
808        if (RenderObject* renderer = node->renderer())
809            return renderer->style()->preserveNewline();
810    }
811
812    return false;
813}
814
815PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
816{
817    if (!context)
818        return 0;
819
820    Document& document = context->ownerDocument();
821    RefPtr<DocumentFragment> fragment = document.createDocumentFragment();
822
823    if (text.isEmpty())
824        return fragment.release();
825
826    String string = text;
827    string.replace("\r\n", "\n");
828    string.replace('\r', '\n');
829
830    if (shouldPreserveNewline(*context)) {
831        fragment->appendChild(document.createTextNode(string));
832        if (string.endsWith('\n')) {
833            RefPtr<Element> element = createBreakElement(document);
834            element->setAttribute(classAttr, AppleInterchangeNewline);
835            fragment->appendChild(element.release());
836        }
837        return fragment.release();
838    }
839
840    // A string with no newlines gets added inline, rather than being put into a paragraph.
841    if (string.find('\n') == kNotFound) {
842        fillContainerFromString(fragment.get(), string);
843        return fragment.release();
844    }
845
846    // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
847    Node* blockNode = enclosingBlock(context->firstNode());
848    Element* block = toElement(blockNode);
849    bool useClonesOfEnclosingBlock = blockNode
850        && blockNode->isElementNode()
851        && !block->hasTagName(bodyTag)
852        && !isHTMLHtmlElement(block)
853        && block != editableRootForPosition(context->startPosition());
854    bool useLineBreak = enclosingTextFormControl(context->startPosition());
855
856    Vector<String> list;
857    string.split('\n', true, list); // true gets us empty strings in the list
858    size_t numLines = list.size();
859    for (size_t i = 0; i < numLines; ++i) {
860        const String& s = list[i];
861
862        RefPtr<Element> element;
863        if (s.isEmpty() && i + 1 == numLines) {
864            // For last line, use the "magic BR" rather than a P.
865            element = createBreakElement(document);
866            element->setAttribute(classAttr, AppleInterchangeNewline);
867        } else if (useLineBreak) {
868            element = createBreakElement(document);
869            fillContainerFromString(fragment.get(), s);
870        } else {
871            if (useClonesOfEnclosingBlock)
872                element = block->cloneElementWithoutChildren();
873            else
874                element = createDefaultParagraphElement(document);
875            fillContainerFromString(element.get(), s);
876        }
877        fragment->appendChild(element.release());
878    }
879    return fragment.release();
880}
881
882PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
883{
884    if (!document)
885        return 0;
886
887    RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
888
889    size_t size = nodes.size();
890    for (size_t i = 0; i < size; ++i) {
891        RefPtr<Element> element = createDefaultParagraphElement(*document);
892        element->appendChild(nodes[i]);
893        fragment->appendChild(element.release());
894    }
895
896    return fragment.release();
897}
898
899String createFullMarkup(const Node* node)
900{
901    if (!node)
902        return String();
903
904    Frame* frame = node->document().frame();
905    if (!frame)
906        return String();
907
908    // FIXME: This is never "for interchange". Is that right?
909    String markupString = createMarkup(node, IncludeNode, 0);
910    Node::NodeType nodeType = node->nodeType();
911    if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
912        markupString = frame->documentTypeString() + markupString;
913
914    return markupString;
915}
916
917String createFullMarkup(const Range* range)
918{
919    if (!range)
920        return String();
921
922    Node* node = range->startContainer();
923    if (!node)
924        return String();
925
926    Frame* frame = node->document().frame();
927    if (!frame)
928        return String();
929
930    // FIXME: This is always "for interchange". Is that right? See the previous method.
931    return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange);
932}
933
934String urlToMarkup(const KURL& url, const String& title)
935{
936    StringBuilder markup;
937    markup.append("<a href=\"");
938    markup.append(url.string());
939    markup.append("\">");
940    MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
941    markup.append("</a>");
942    return markup.toString();
943}
944
945PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, Element* contextElement, ParserContentPolicy parserContentPolicy, const char* method, ExceptionState& exceptionState)
946{
947    Document& document = contextElement->hasTagName(templateTag) ? contextElement->document().ensureTemplateDocument() : contextElement->document();
948    RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
949
950    if (document.isHTMLDocument()) {
951        fragment->parseHTML(markup, contextElement, parserContentPolicy);
952        return fragment;
953    }
954
955    bool wasValid = fragment->parseXML(markup, contextElement, parserContentPolicy);
956    if (!wasValid) {
957        exceptionState.throwDOMException(SyntaxError, "The provided markup is invalid XML, and therefore cannot be inserted into an XML document.");
958        return 0;
959    }
960    return fragment.release();
961}
962
963PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sourceString, const String& sourceMIMEType, Document& outputDoc)
964{
965    RefPtr<DocumentFragment> fragment = outputDoc.createDocumentFragment();
966
967    if (sourceMIMEType == "text/html") {
968        // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
969        // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
970        // Unfortunately, that's an implementation detail of the parser.
971        // We achieve that effect here by passing in a fake body element as context for the fragment.
972        RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
973        fragment->parseHTML(sourceString, fakeBody.get());
974    } else if (sourceMIMEType == "text/plain") {
975        fragment->parserAppendChild(Text::create(outputDoc, sourceString));
976    } else {
977        bool successfulParse = fragment->parseXML(sourceString, 0);
978        if (!successfulParse)
979            return 0;
980    }
981
982    // FIXME: Do we need to mess with URLs here?
983
984    return fragment.release();
985}
986
987static inline void removeElementPreservingChildren(PassRefPtr<DocumentFragment> fragment, HTMLElement* element)
988{
989    RefPtr<Node> nextChild;
990    for (RefPtr<Node> child = element->firstChild(); child; child = nextChild) {
991        nextChild = child->nextSibling();
992        element->removeChild(child.get());
993        fragment->insertBefore(child, element);
994    }
995    fragment->removeChild(element);
996}
997
998PassRefPtr<DocumentFragment> createContextualFragment(const String& markup, HTMLElement* element, ParserContentPolicy parserContentPolicy, ExceptionState& exceptionState)
999{
1000    ASSERT(element);
1001    if (element->ieForbidsInsertHTML() || element->hasLocalName(colTag) || element->hasLocalName(colgroupTag) || element->hasLocalName(framesetTag)
1002        || element->hasLocalName(headTag) || element->hasLocalName(styleTag) || element->hasLocalName(titleTag)) {
1003        exceptionState.throwDOMException(NotSupportedError, "The range's container is '" + element->localName() + "', which is not supported.");
1004        return 0;
1005    }
1006
1007    RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, element, parserContentPolicy, "createContextualFragment", exceptionState);
1008    if (!fragment)
1009        return 0;
1010
1011    // We need to pop <html> and <body> elements and remove <head> to
1012    // accommodate folks passing complete HTML documents to make the
1013    // child of an element.
1014
1015    RefPtr<Node> nextNode;
1016    for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) {
1017        nextNode = node->nextSibling();
1018        if (isHTMLHtmlElement(node.get()) || node->hasTagName(headTag) || node->hasTagName(bodyTag)) {
1019            HTMLElement* element = toHTMLElement(node);
1020            if (Node* firstChild = element->firstChild())
1021                nextNode = firstChild;
1022            removeElementPreservingChildren(fragment, element);
1023        }
1024    }
1025    return fragment.release();
1026}
1027
1028void replaceChildrenWithFragment(ContainerNode* container, PassRefPtr<DocumentFragment> fragment, ExceptionState& exceptionState)
1029{
1030    ASSERT(container);
1031    RefPtr<ContainerNode> containerNode(container);
1032
1033    ChildListMutationScope mutation(*containerNode);
1034
1035    if (!fragment->firstChild()) {
1036        containerNode->removeChildren();
1037        return;
1038    }
1039
1040    if (containerNode->hasOneTextChild() && fragment->hasOneTextChild()) {
1041        toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data());
1042        return;
1043    }
1044
1045    if (containerNode->hasOneChild()) {
1046        containerNode->replaceChild(fragment, containerNode->firstChild(), exceptionState);
1047        return;
1048    }
1049
1050    containerNode->removeChildren();
1051    containerNode->appendChild(fragment, exceptionState);
1052}
1053
1054void replaceChildrenWithText(ContainerNode* container, const String& text, ExceptionState& exceptionState)
1055{
1056    ASSERT(container);
1057    RefPtr<ContainerNode> containerNode(container);
1058
1059    ChildListMutationScope mutation(*containerNode);
1060
1061    if (containerNode->hasOneTextChild()) {
1062        toText(containerNode->firstChild())->setData(text);
1063        return;
1064    }
1065
1066    RefPtr<Text> textNode = Text::create(containerNode->document(), text);
1067
1068    if (containerNode->hasOneChild()) {
1069        containerNode->replaceChild(textNode.release(), containerNode->firstChild(), exceptionState);
1070        return;
1071    }
1072
1073    containerNode->removeChildren();
1074    containerNode->appendChild(textNode.release(), exceptionState);
1075}
1076
1077void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionState& exceptionState)
1078{
1079    ASSERT(node && node->isTextNode());
1080    Node* next = node->nextSibling();
1081    if (!next || !next->isTextNode())
1082        return;
1083
1084    RefPtr<Text> textNode = toText(node.get());
1085    RefPtr<Text> textNext = toText(next);
1086    textNode->appendData(textNext->data());
1087    if (textNext->parentNode()) // Might have been removed by mutation event.
1088        textNext->remove(exceptionState);
1089}
1090
1091}
1092