5b705d9ceb7c09036ba5e0777a88ae4e7a44f72a
[WebKit-https.git] / Source / WebCore / html / HTMLViewSourceDocument.cpp
1 /*
2  * Copyright (C) 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
20  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "HTMLViewSourceDocument.h"
27
28 #include "DOMImplementation.h"
29 #include "DocumentStyleSheetCollection.h"
30 #include "HTMLAnchorElement.h"
31 #include "HTMLBRElement.h"
32 #include "HTMLBaseElement.h"
33 #include "HTMLBodyElement.h"
34 #include "HTMLDivElement.h"
35 #include "HTMLHtmlElement.h"
36 #include "HTMLTableCellElement.h"
37 #include "HTMLTableElement.h"
38 #include "HTMLTableRowElement.h"
39 #include "HTMLTableSectionElement.h"
40 #include "Text.h"
41 #include "TextViewSourceParser.h"
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
47 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const URL& url, const String& mimeType)
48     : HTMLDocument(frame, url)
49     , m_type(mimeType)
50 {
51     styleSheetCollection().setUsesBeforeAfterRulesOverride(true);
52     setIsViewSource(true);
53
54     setCompatibilityMode(QuirksMode);
55     lockCompatibilityMode();
56 }
57
58 PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser()
59 {
60     if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type))
61         return HTMLViewSourceParser::create(*this);
62
63     return TextViewSourceParser::create(*this);
64 }
65
66 void HTMLViewSourceDocument::createContainingTable()
67 {
68     RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(*this);
69     parserAppendChild(html);
70     RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(*this);
71     html->parserAppendChild(body);
72     // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole
73     // document.
74     RefPtr<HTMLDivElement> div = HTMLDivElement::create(*this);
75     div->setAttribute(classAttr, "webkit-line-gutter-backdrop");
76     body->parserAppendChild(div);
77
78     RefPtr<HTMLTableElement> table = HTMLTableElement::create(*this);
79     body->parserAppendChild(table);
80     m_tbody = HTMLTableSectionElement::create(tbodyTag, *this);
81     table->parserAppendChild(m_tbody);
82     m_current = m_tbody;
83 }
84
85 void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token)
86 {
87     if (!m_current)
88         createContainingTable();
89
90     switch (token.type()) {
91     case HTMLToken::Uninitialized:
92         ASSERT_NOT_REACHED();
93         break;
94     case HTMLToken::DOCTYPE:
95         processDoctypeToken(source, token);
96         break;
97     case HTMLToken::EndOfFile:
98         if (!m_tbody->hasChildNodes())
99             addLine(String());
100         break;
101     case HTMLToken::StartTag:
102     case HTMLToken::EndTag:
103         processTagToken(source, token);
104         break;
105     case HTMLToken::Comment:
106         processCommentToken(source, token);
107         break;
108     case HTMLToken::Character:
109         processCharacterToken(source, token);
110         break;
111     }
112 }
113
114 void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&)
115 {
116     if (!m_current)
117         createContainingTable();
118     m_current = addSpanWithClassName("webkit-html-doctype");
119     addText(source, "webkit-html-doctype");
120     m_current = m_td;
121 }
122
123 void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token)
124 {
125     m_current = addSpanWithClassName("webkit-html-tag");
126
127     AtomicString tagName(token.name());
128
129     unsigned index = 0;
130     HTMLToken::AttributeList::const_iterator iter = token.attributes().begin();
131     while (index < source.length()) {
132         if (iter == token.attributes().end()) {
133             // We want to show the remaining characters in the token.
134             index = addRange(source, index, source.length(), "");
135             ASSERT(index == source.length());
136             break;
137         }
138
139         AtomicString name(iter->name);
140         String value = StringImpl::create8BitIfPossible(iter->value);
141
142         index = addRange(source, index, iter->nameRange.start - token.startIndex(), "");
143         index = addRange(source, index, iter->nameRange.end - token.startIndex(), "webkit-html-attribute-name");
144
145         if (tagName == baseTag && name == hrefAttr)
146             m_current = addBase(value);
147
148         index = addRange(source, index, iter->valueRange.start - token.startIndex(), "");
149
150         bool isLink = name == srcAttr || name == hrefAttr;
151         index = addRange(source, index, iter->valueRange.end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag, value);
152
153         ++iter;
154     }
155     m_current = m_td;
156 }
157
158 void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&)
159 {
160     m_current = addSpanWithClassName("webkit-html-comment");
161     addText(source, "webkit-html-comment");
162     m_current = m_td;
163 }
164
165 void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&)
166 {
167     addText(source, "");
168 }
169
170 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className)
171 {
172     if (m_current == m_tbody) {
173         addLine(className);
174         return m_current;
175     }
176
177     RefPtr<HTMLElement> span = HTMLElement::create(spanTag, *this);
178     span->setAttribute(classAttr, className);
179     m_current->parserAppendChild(span);
180     return span.release();
181 }
182
183 void HTMLViewSourceDocument::addLine(const AtomicString& className)
184 {
185     // Create a table row.
186     RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(*this);
187     m_tbody->parserAppendChild(trow);
188
189     // Create a cell that will hold the line number (it is generated in the stylesheet using counters).
190     RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, *this);
191     td->setAttribute(classAttr, "webkit-line-number");
192     trow->parserAppendChild(td);
193
194     // Create a second cell for the line contents
195     td = HTMLTableCellElement::create(tdTag, *this);
196     td->setAttribute(classAttr, "webkit-line-content");
197     trow->parserAppendChild(td);
198     m_current = m_td = td;
199
200 #ifdef DEBUG_LINE_NUMBERS
201     RefPtr<Text> lineNumberText = Text::create(*this, String::number(parser()->lineNumber() + 1) + " ");
202     td->addChild(lineNumberText);
203 #endif
204
205     // Open up the needed spans.
206     if (!className.isEmpty()) {
207         if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value")
208             m_current = addSpanWithClassName("webkit-html-tag");
209         m_current = addSpanWithClassName(className);
210     }
211 }
212
213 void HTMLViewSourceDocument::finishLine()
214 {
215     if (!m_current->hasChildNodes()) {
216         RefPtr<HTMLBRElement> br = HTMLBRElement::create(*this);
217         m_current->parserAppendChild(br);
218     }
219     m_current = m_tbody;
220 }
221
222 void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className)
223 {
224     if (text.isEmpty())
225         return;
226
227     // Add in the content, splitting on newlines.
228     Vector<String> lines;
229     text.split('\n', true, lines);
230     unsigned size = lines.size();
231     for (unsigned i = 0; i < size; i++) {
232         String substring = lines[i];
233         if (m_current == m_tbody)
234             addLine(className);
235         if (substring.isEmpty()) {
236             if (i == size - 1)
237                 break;
238             finishLine();
239             continue;
240         }
241         RefPtr<Text> text = Text::create(*this, substring);
242         m_current->parserAppendChild(text);
243         if (i < size - 1)
244             finishLine();
245     }
246 }
247
248 int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor, const String& link)
249 {
250     ASSERT(start <= end);
251     if (start == end)
252         return start;
253
254     String text = source.substring(start, end - start);
255     if (!className.isEmpty()) {
256         if (isLink)
257             m_current = addLink(link, isAnchor);
258         else
259             m_current = addSpanWithClassName(className);
260     }
261     addText(text, className);
262     if (!className.isEmpty() && m_current != m_tbody)
263         m_current = toElement(m_current->parentNode());
264     return end;
265 }
266
267 PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href)
268 {
269     RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, *this);
270     base->setAttribute(hrefAttr, href);
271     m_current->parserAppendChild(base);
272     return base.release();
273 }
274
275 PassRefPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor)
276 {
277     if (m_current == m_tbody)
278         addLine("webkit-html-tag");
279
280     // Now create a link for the attribute value instead of a span.
281     RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(*this);
282     const char* classValue;
283     if (isAnchor)
284         classValue = "webkit-html-attribute-value webkit-html-external-link";
285     else
286         classValue = "webkit-html-attribute-value webkit-html-resource-link";
287     anchor->setAttribute(classAttr, classValue);
288     anchor->setAttribute(targetAttr, "_blank");
289     anchor->setAttribute(hrefAttr, url);
290     m_current->parserAppendChild(anchor);
291     return anchor.release();
292 }
293
294 }