1 /*
2 * Copyright (C) 2006, 2008 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 "HTMLAnchorElement.h"
30 #include "HTMLBodyElement.h"
31 #include "HTMLDivElement.h"
32 #include "HTMLHtmlElement.h"
33 #include "HTMLNames.h"
34 #include "HTMLTableCellElement.h"
35 #include "HTMLTableElement.h"
36 #include "HTMLTableRowElement.h"
37 #include "HTMLTableSectionElement.h"
38 #include "HTMLTokenizer.h"
39 #include "MappedAttribute.h"
40 #include "Text.h"
41 #include "TextDocument.h"
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
HTMLViewSourceDocument(Frame * frame,const String & mimeType)47 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const String& mimeType)
48 : HTMLDocument(frame)
49 , m_type(mimeType)
50 , m_current(0)
51 , m_tbody(0)
52 , m_td(0)
53 {
54 setUsesBeforeAfterRules(true);
55 }
56
createTokenizer()57 Tokenizer* HTMLViewSourceDocument::createTokenizer()
58 {
59 // Use HTMLTokenizer if applicable, otherwise use TextTokenizer.
60 if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || implementation()->isXMLMIMEType(m_type)
61 #if ENABLE(XHTMLMP)
62 || m_type == "application/vnd.wap.xhtml+xml"
63 #endif
64 ) {
65 return new HTMLTokenizer(this);
66 }
67
68 return createTextTokenizer(this);
69 }
70
createContainingTable()71 void HTMLViewSourceDocument::createContainingTable()
72 {
73 RefPtr<Element> html = new HTMLHtmlElement(htmlTag, this);
74 addChild(html);
75 html->attach();
76 RefPtr<Element> body = new HTMLBodyElement(bodyTag, this);
77 html->addChild(body);
78 body->attach();
79
80 // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole
81 // document.
82 RefPtr<Element> div = new HTMLDivElement(divTag, this);
83 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create();
84 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-gutter-backdrop"));
85 div->setAttributeMap(attrs.release());
86 body->addChild(div);
87 div->attach();
88
89 RefPtr<Element> table = new HTMLTableElement(tableTag, this);
90 body->addChild(table);
91 table->attach();
92 m_tbody = new HTMLTableSectionElement(tbodyTag, this);
93 table->addChild(m_tbody);
94 m_tbody->attach();
95 m_current = m_tbody;
96 }
97
addViewSourceText(const String & text)98 void HTMLViewSourceDocument::addViewSourceText(const String& text)
99 {
100 if (!m_current)
101 createContainingTable();
102 addText(text, "");
103 }
104
addViewSourceToken(Token * token)105 void HTMLViewSourceDocument::addViewSourceToken(Token* token)
106 {
107 if (!m_current)
108 createContainingTable();
109
110 if (token->tagName == textAtom)
111 addText(token->text.get(), "");
112 else if (token->tagName == commentAtom) {
113 if (token->beginTag) {
114 m_current = addSpanWithClassName("webkit-html-comment");
115 addText(String("<!--") + token->text.get() + "-->", "webkit-html-comment");
116 }
117 } else {
118 // Handle the tag.
119 String classNameStr = "webkit-html-tag";
120 m_current = addSpanWithClassName(classNameStr);
121
122 String text = "<";
123 if (!token->beginTag)
124 text += "/";
125 text += token->tagName;
126 Vector<UChar>* guide = token->m_sourceInfo.get();
127 if (!guide || !guide->size())
128 text += ">";
129
130 addText(text, classNameStr);
131
132 // Walk our guide string that tells us where attribute names/values should go.
133 if (guide && guide->size()) {
134 unsigned size = guide->size();
135 unsigned begin = 0;
136 unsigned currAttr = 0;
137 RefPtr<Attribute> attr = 0;
138 for (unsigned i = 0; i < size; i++) {
139 if (guide->at(i) == 'a' || guide->at(i) == 'x' || guide->at(i) == 'v') {
140 // Add in the string.
141 addText(String(static_cast<UChar*>(guide->data()) + begin, i - begin), classNameStr);
142
143 begin = i + 1;
144
145 if (guide->at(i) == 'a') {
146 if (token->attrs && currAttr < token->attrs->length())
147 attr = token->attrs->attributeItem(currAttr++);
148 else
149 attr = 0;
150 }
151 if (attr) {
152 if (guide->at(i) == 'a') {
153 String name = attr->name().toString();
154
155 m_current = addSpanWithClassName("webkit-html-attribute-name");
156 addText(name, "webkit-html-attribute-name");
157 if (m_current != m_tbody)
158 m_current = static_cast<Element*>(m_current->parent());
159 } else {
160 const String& value = attr->value().string();
161
162 // Compare ignoring case since HTMLTokenizer doesn't
163 // lower names when passing in tokens to
164 // HTMLViewSourceDocument.
165 if (equalIgnoringCase(token->tagName, "base") && equalIgnoringCase(attr->name().localName(), "href")) {
166 // Catch the href attribute in the base element.
167 // It will be used for rendering anchors created
168 // by addLink() below.
169 setBaseElementURL(KURL(url(), value));
170 }
171
172 // FIXME: XML could use namespace prefixes and confuse us.
173 if (equalIgnoringCase(attr->name().localName(), "src") || equalIgnoringCase(attr->name().localName(), "href"))
174 m_current = addLink(value, equalIgnoringCase(token->tagName, "a"));
175 else
176 m_current = addSpanWithClassName("webkit-html-attribute-value");
177 addText(value, "webkit-html-attribute-value");
178 if (m_current != m_tbody)
179 m_current = static_cast<Element*>(m_current->parent());
180 }
181 }
182 }
183 }
184
185 // Add in any string that might be left.
186 if (begin < size)
187 addText(String(static_cast<UChar*>(guide->data()) + begin, size - begin), classNameStr);
188
189 // Add in the end tag.
190 addText(">", classNameStr);
191 }
192
193 m_current = m_td;
194 }
195 }
196
addViewSourceDoctypeToken(DoctypeToken * doctypeToken)197 void HTMLViewSourceDocument::addViewSourceDoctypeToken(DoctypeToken* doctypeToken)
198 {
199 if (!m_current)
200 createContainingTable();
201 m_current = addSpanWithClassName("webkit-html-doctype");
202 String text = "<";
203 text += String::adopt(doctypeToken->m_source);
204 text += ">";
205 addText(text, "webkit-html-doctype");
206 }
207
addSpanWithClassName(const String & className)208 Element* HTMLViewSourceDocument::addSpanWithClassName(const String& className)
209 {
210 if (m_current == m_tbody) {
211 addLine(className);
212 return m_current;
213 }
214
215 Element* span = new HTMLElement(spanTag, this);
216 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create();
217 attrs->addAttribute(MappedAttribute::create(classAttr, className));
218 span->setAttributeMap(attrs.release());
219 m_current->addChild(span);
220 span->attach();
221 return span;
222 }
223
addLine(const String & className)224 void HTMLViewSourceDocument::addLine(const String& className)
225 {
226 // Create a table row.
227 RefPtr<Element> trow = new HTMLTableRowElement(trTag, this);
228 m_tbody->addChild(trow);
229 trow->attach();
230
231 // Create a cell that will hold the line number (it is generated in the stylesheet using counters).
232 Element* td = new HTMLTableCellElement(tdTag, this);
233 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create();
234 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-number"));
235 td->setAttributeMap(attrs.release());
236 trow->addChild(td);
237 td->attach();
238
239 // Create a second cell for the line contents
240 td = new HTMLTableCellElement(tdTag, this);
241 attrs = NamedMappedAttrMap::create();
242 attrs->addAttribute(MappedAttribute::create(classAttr, "webkit-line-content"));
243 td->setAttributeMap(attrs.release());
244 trow->addChild(td);
245 td->attach();
246 m_current = m_td = td;
247
248 #ifdef DEBUG_LINE_NUMBERS
249 RefPtr<Text> lineNumberText = new Text(this, String::number(tokenizer()->lineNumber() + 1) + " ");
250 td->addChild(lineNumberText);
251 lineNumberText->attach();
252 #endif
253
254 // Open up the needed spans.
255 if (!className.isEmpty()) {
256 if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value")
257 m_current = addSpanWithClassName("webkit-html-tag");
258 m_current = addSpanWithClassName(className);
259 }
260 }
261
addText(const String & text,const String & className)262 void HTMLViewSourceDocument::addText(const String& text, const String& className)
263 {
264 if (text.isEmpty())
265 return;
266
267 // Add in the content, splitting on newlines.
268 Vector<String> lines;
269 text.split('\n', true, lines);
270 unsigned size = lines.size();
271 for (unsigned i = 0; i < size; i++) {
272 String substring = lines[i];
273 if (substring.isEmpty()) {
274 if (i == size - 1)
275 break;
276 substring = " ";
277 }
278 if (m_current == m_tbody)
279 addLine(className);
280 RefPtr<Text> t = new Text(this, substring);
281 m_current->addChild(t);
282 t->attach();
283 if (i < size - 1)
284 m_current = m_tbody;
285 }
286
287 // Set current to m_tbody if the last character was a newline.
288 if (text[text.length() - 1] == '\n')
289 m_current = m_tbody;
290 }
291
addLink(const String & url,bool isAnchor)292 Element* HTMLViewSourceDocument::addLink(const String& url, bool isAnchor)
293 {
294 if (m_current == m_tbody)
295 addLine("webkit-html-tag");
296
297 // Now create a link for the attribute value instead of a span.
298 Element* anchor = new HTMLAnchorElement(aTag, this);
299 RefPtr<NamedMappedAttrMap> attrs = NamedMappedAttrMap::create();
300 const char* classValue;
301 if (isAnchor)
302 classValue = "webkit-html-attribute-value webkit-html-external-link";
303 else
304 classValue = "webkit-html-attribute-value webkit-html-resource-link";
305 attrs->addAttribute(MappedAttribute::create(classAttr, classValue));
306 attrs->addAttribute(MappedAttribute::create(targetAttr, "_blank"));
307 attrs->addAttribute(MappedAttribute::create(hrefAttr, url));
308 anchor->setAttributeMap(attrs.release());
309 m_current->addChild(anchor);
310 anchor->attach();
311 return anchor;
312 }
313
314 }
315