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