• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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