Home | History | Annotate | Download | only in html
      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 
     47 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const String& mimeType)
     48     : HTMLDocument(frame)
     49     , m_type(mimeType)
     50 {
     51     setUsesBeforeAfterRules(true);
     52 }
     53 
     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 
     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 
     95 void HTMLViewSourceDocument::addViewSourceText(const String& text)
     96 {
     97     if (!m_current)
     98         createContainingTable();
     99     addText(text, "");
    100 }
    101 
    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 
    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 
    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 
    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 
    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 
    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