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