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