initial import
[vuplus_webkit] / 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 "Attribute.h"
29 #include "DOMImplementation.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 "HTMLNames.h"
37 #include "HTMLTableCellElement.h"
38 #include "HTMLTableElement.h"
39 #include "HTMLTableRowElement.h"
40 #include "HTMLTableSectionElement.h"
41 #include "HTMLToken.h"
42 #include "HTMLViewSourceParser.h"
43 #include "SegmentedString.h"
44 #include "Text.h"
45 #include "TextViewSourceParser.h"
46
47 namespace WebCore {
48
49 using namespace HTMLNames;
50
51 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, const String& mimeType)
52     : HTMLDocument(frame, url)
53     , m_type(mimeType)
54 {
55     setUsesBeforeAfterRules(true);
56     setIsViewSource(true);
57
58     setCompatibilityMode(QuirksMode);
59     lockCompatibilityMode();
60 }
61
62 PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser()
63 {
64     if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type)
65 #if ENABLE(XHTMLMP)
66         || m_type == "application/vnd.wap.xhtml+xml"
67 #endif
68         )
69         return HTMLViewSourceParser::create(this);
70
71     return TextViewSourceParser::create(this);
72 }
73
74 void HTMLViewSourceDocument::createContainingTable()
75 {
76     RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(this);
77     parserAddChild(html);
78     html->attach();
79     RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(this);
80     html->parserAddChild(body);
81     body->attach();
82
83     // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole
84     // document.
85     RefPtr<HTMLDivElement> div = HTMLDivElement::create(this);
86     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
87     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-gutter-backdrop"));
88     div->setAttributeMap(attrs.release());
89     body->parserAddChild(div);
90     div->attach();
91
92     RefPtr<HTMLTableElement> table = HTMLTableElement::create(this);
93     body->parserAddChild(table);
94     table->attach();
95     m_tbody = HTMLTableSectionElement::create(tbodyTag, this);
96     table->parserAddChild(m_tbody);
97     m_tbody->attach();
98     m_current = m_tbody;
99 }
100
101 void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token)
102 {
103     if (!m_current)
104         createContainingTable();
105
106     switch (token.type()) {
107     case HTMLTokenTypes::Uninitialized:
108         ASSERT_NOT_REACHED();
109         break;
110     case HTMLTokenTypes::DOCTYPE:
111         processDoctypeToken(source, token);
112         break;
113     case HTMLTokenTypes::EndOfFile:
114         if (!m_tbody->hasChildNodes())
115             addLine(String());
116         break;
117     case HTMLTokenTypes::StartTag:
118     case HTMLTokenTypes::EndTag:
119         processTagToken(source, token);
120         break;
121     case HTMLTokenTypes::Comment:
122         processCommentToken(source, token);
123         break;
124     case HTMLTokenTypes::Character:
125         processCharacterToken(source, token);
126         break;
127     }
128 }
129
130 void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&)
131 {
132     if (!m_current)
133         createContainingTable();
134     m_current = addSpanWithClassName("webkit-html-doctype");
135     addText(source, "webkit-html-doctype");
136     m_current = m_td;
137 }
138
139 void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token)
140 {
141     m_current = addSpanWithClassName("webkit-html-tag");
142
143     AtomicString tagName(token.name().data(), token.name().size());
144
145     unsigned index = 0;
146     HTMLToken::AttributeList::const_iterator iter = token.attributes().begin();
147     while (index < source.length()) {
148         if (iter == token.attributes().end()) {
149             // We want to show the remaining characters in the token.
150             index = addRange(source, index, source.length(), "");
151             ASSERT(index == source.length());
152             break;
153         }
154
155         AtomicString name(iter->m_name.data(), iter->m_name.size());
156         String value(iter->m_value.data(), iter->m_value.size()); 
157
158         index = addRange(source, index, iter->m_nameRange.m_start - token.startIndex(), "");
159         index = addRange(source, index, iter->m_nameRange.m_end - token.startIndex(), "webkit-html-attribute-name");
160
161         if (tagName == baseTag && name == hrefAttr)
162             m_current = addBase(value);
163
164         index = addRange(source, index, iter->m_valueRange.m_start - token.startIndex(), "");
165
166         bool isLink = name == srcAttr || name == hrefAttr;
167         index = addRange(source, index, iter->m_valueRange.m_end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag);
168
169         ++iter;
170     }
171     m_current = m_td;
172 }
173
174 void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&)
175 {
176     m_current = addSpanWithClassName("webkit-html-comment");
177     addText(source, "webkit-html-comment");
178     m_current = m_td;
179 }
180
181 void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&)
182 {
183     addText(source, "");
184 }
185
186 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className)
187 {
188     if (m_current == m_tbody) {
189         addLine(className);
190         return m_current;
191     }
192
193     RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this);
194     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
195     attrs->addAttribute(Attribute::createMapped(classAttr, className));
196     span->setAttributeMap(attrs.release());
197     m_current->parserAddChild(span);
198     span->attach();
199     return span.release();
200 }
201
202 void HTMLViewSourceDocument::addLine(const AtomicString& className)
203 {
204     // Create a table row.
205     RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(this);
206     m_tbody->parserAddChild(trow);
207     trow->attach();
208
209     // Create a cell that will hold the line number (it is generated in the stylesheet using counters).
210     RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, this);
211     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
212     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-number"));
213     td->setAttributeMap(attrs.release());
214     trow->parserAddChild(td);
215     td->attach();
216
217     // Create a second cell for the line contents
218     td = HTMLTableCellElement::create(tdTag, this);
219     attrs = NamedNodeMap::create();
220     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-content"));
221     td->setAttributeMap(attrs.release());
222     trow->parserAddChild(td);
223     td->attach();
224     m_current = m_td = td;
225
226 #ifdef DEBUG_LINE_NUMBERS
227     RefPtr<Text> lineNumberText = Text::create(this, String::number(parser()->lineNumber() + 1) + " ");
228     td->addChild(lineNumberText);
229     lineNumberText->attach();
230 #endif
231
232     // Open up the needed spans.
233     if (!className.isEmpty()) {
234         if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value")
235             m_current = addSpanWithClassName("webkit-html-tag");
236         m_current = addSpanWithClassName(className);
237     }
238 }
239
240 void HTMLViewSourceDocument::finishLine()
241 {
242     if (!m_current->hasChildNodes()) {
243         RefPtr<HTMLBRElement> br = HTMLBRElement::create(this);
244         m_current->parserAddChild(br);
245         br->attach();
246     }
247     m_current = m_tbody;
248 }
249
250 void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className)
251 {
252     if (text.isEmpty())
253         return;
254
255     // Add in the content, splitting on newlines.
256     Vector<String> lines;
257     text.split('\n', true, lines);
258     unsigned size = lines.size();
259     for (unsigned i = 0; i < size; i++) {
260         String substring = lines[i];
261         if (m_current == m_tbody)
262             addLine(className);
263         if (substring.isEmpty()) {
264             if (i == size - 1)
265                 break;
266             finishLine();
267             continue;
268         }
269         RefPtr<Text> t = Text::create(this, substring);
270         m_current->parserAddChild(t);
271         t->attach();
272         if (i < size - 1)
273             finishLine();
274     }
275 }
276
277 int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor)
278 {
279     ASSERT(start <= end);
280     if (start == end)
281         return start;
282
283     String text = source.substring(start, end - start);
284     if (!className.isEmpty()) {
285         if (isLink)
286             m_current = addLink(text, isAnchor);
287         else
288             m_current = addSpanWithClassName(className);
289     }
290     addText(text, className);
291     if (!className.isEmpty() && m_current != m_tbody)
292         m_current = static_cast<Element*>(m_current->parentNode());
293     return end;
294 }
295
296 PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href)
297 {
298     RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, this);
299     RefPtr<NamedNodeMap> attributeMap = NamedNodeMap::create();
300     attributeMap->addAttribute(Attribute::createMapped(hrefAttr, href));
301     base->setAttributeMap(attributeMap.release());
302     m_current->parserAddChild(base);
303     base->attach();
304     return base.release();
305 }
306
307 PassRefPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor)
308 {
309     if (m_current == m_tbody)
310         addLine("webkit-html-tag");
311
312     // Now create a link for the attribute value instead of a span.
313     RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(this);
314     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
315     const char* classValue;
316     if (isAnchor)
317         classValue = "webkit-html-attribute-value webkit-html-external-link";
318     else
319         classValue = "webkit-html-attribute-value webkit-html-resource-link";
320     attrs->addAttribute(Attribute::createMapped(classAttr, classValue));
321     attrs->addAttribute(Attribute::createMapped(targetAttr, "_blank"));
322     attrs->addAttribute(Attribute::createMapped(hrefAttr, url));
323     anchor->setAttributeMap(attrs.release());
324     m_current->parserAddChild(anchor);
325     anchor->attach();
326     return anchor.release();
327 }
328
329 }