Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2006, 2008, 2009, 2010 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 "Attribute.h"
     29 #include "DOMImplementation.h"
     30 #include "HTMLAnchorElement.h"
     31 #include "HTMLBaseElement.h"
     32 #include "HTMLBodyElement.h"
     33 #include "HTMLDivElement.h"
     34 #include "HTMLHtmlElement.h"
     35 #include "HTMLNames.h"
     36 #include "HTMLTableCellElement.h"
     37 #include "HTMLTableElement.h"
     38 #include "HTMLTableRowElement.h"
     39 #include "HTMLTableSectionElement.h"
     40 #include "HTMLToken.h"
     41 #include "HTMLViewSourceParser.h"
     42 #include "SegmentedString.h"
     43 #include "Text.h"
     44 #include "TextViewSourceParser.h"
     45 
     46 namespace WebCore {
     47 
     48 using namespace HTMLNames;
     49 
     50 HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, const String& mimeType)
     51     : HTMLDocument(frame, url)
     52     , m_type(mimeType)
     53 {
     54     setUsesBeforeAfterRules(true);
     55     setUsesViewSourceStyles(true);
     56 
     57     setCompatibilityMode(QuirksMode);
     58     lockCompatibilityMode();
     59 }
     60 
     61 PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser()
     62 {
     63     if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type)
     64 #if ENABLE(XHTMLMP)
     65         || m_type == "application/vnd.wap.xhtml+xml"
     66 #endif
     67         )
     68         return HTMLViewSourceParser::create(this);
     69 
     70     return TextViewSourceParser::create(this);
     71 }
     72 
     73 void HTMLViewSourceDocument::createContainingTable()
     74 {
     75     RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(this);
     76     parserAddChild(html);
     77     html->attach();
     78     RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(this);
     79     html->parserAddChild(body);
     80     body->attach();
     81 
     82     // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole
     83     // document.
     84     RefPtr<HTMLDivElement> div = HTMLDivElement::create(this);
     85     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
     86     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-gutter-backdrop"));
     87     div->setAttributeMap(attrs.release());
     88     body->parserAddChild(div);
     89     div->attach();
     90 
     91     RefPtr<HTMLTableElement> table = HTMLTableElement::create(this);
     92     body->parserAddChild(table);
     93     table->attach();
     94     m_tbody = HTMLTableSectionElement::create(tbodyTag, this);
     95     table->parserAddChild(m_tbody);
     96     m_tbody->attach();
     97     m_current = m_tbody;
     98 }
     99 
    100 void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token)
    101 {
    102     if (!m_current)
    103         createContainingTable();
    104 
    105     switch (token.type()) {
    106     case HTMLToken::Uninitialized:
    107         ASSERT_NOT_REACHED();
    108         break;
    109     case HTMLToken::DOCTYPE:
    110         processDoctypeToken(source, token);
    111         break;
    112     case HTMLToken::EndOfFile:
    113         break;
    114     case HTMLToken::StartTag:
    115     case HTMLToken::EndTag:
    116         processTagToken(source, token);
    117         break;
    118     case HTMLToken::Comment:
    119         processCommentToken(source, token);
    120         break;
    121     case HTMLToken::Character:
    122         processCharacterToken(source, token);
    123         break;
    124     }
    125 }
    126 
    127 void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&)
    128 {
    129     if (!m_current)
    130         createContainingTable();
    131     m_current = addSpanWithClassName("webkit-html-doctype");
    132     addText(source, "webkit-html-doctype");
    133     m_current = m_td;
    134 }
    135 
    136 void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token)
    137 {
    138     m_current = addSpanWithClassName("webkit-html-tag");
    139 
    140     AtomicString tagName(token.name().data(), token.name().size());
    141 
    142     unsigned index = 0;
    143     HTMLToken::AttributeList::const_iterator iter = token.attributes().begin();
    144     while (index < source.length()) {
    145         if (iter == token.attributes().end()) {
    146             // We want to show the remaining characters in the token.
    147             index = addRange(source, index, source.length(), "");
    148             ASSERT(index == source.length());
    149             break;
    150         }
    151 
    152         AtomicString name(iter->m_name.data(), iter->m_name.size());
    153         String value(iter->m_value.data(), iter->m_value.size());
    154 
    155         index = addRange(source, index, iter->m_nameRange.m_start - token.startIndex(), "");
    156         index = addRange(source, index, iter->m_nameRange.m_end - token.startIndex(), "webkit-html-attribute-name");
    157 
    158         if (tagName == baseTag && name == hrefAttr)
    159             m_current = addBase(value);
    160 
    161         index = addRange(source, index, iter->m_valueRange.m_start - token.startIndex(), "");
    162 
    163         bool isLink = name == srcAttr || name == hrefAttr;
    164         index = addRange(source, index, iter->m_valueRange.m_end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag);
    165 
    166         ++iter;
    167     }
    168     m_current = m_td;
    169 }
    170 
    171 void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&)
    172 {
    173     m_current = addSpanWithClassName("webkit-html-comment");
    174     addText(source, "webkit-html-comment");
    175     m_current = m_td;
    176 }
    177 
    178 void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&)
    179 {
    180     addText(source, "");
    181 }
    182 
    183 PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className)
    184 {
    185     if (m_current == m_tbody) {
    186         addLine(className);
    187         return m_current;
    188     }
    189 
    190     RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this);
    191     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
    192     attrs->addAttribute(Attribute::createMapped(classAttr, className));
    193     span->setAttributeMap(attrs.release());
    194     m_current->parserAddChild(span);
    195     span->attach();
    196     return span.release();
    197 }
    198 
    199 void HTMLViewSourceDocument::addLine(const AtomicString& className)
    200 {
    201     // Create a table row.
    202     RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(this);
    203     m_tbody->parserAddChild(trow);
    204     trow->attach();
    205 
    206     // Create a cell that will hold the line number (it is generated in the stylesheet using counters).
    207     RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, this);
    208     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
    209     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-number"));
    210     td->setAttributeMap(attrs.release());
    211     trow->parserAddChild(td);
    212     td->attach();
    213 
    214     // Create a second cell for the line contents
    215     td = HTMLTableCellElement::create(tdTag, this);
    216     attrs = NamedNodeMap::create();
    217     attrs->addAttribute(Attribute::createMapped(classAttr, "webkit-line-content"));
    218     td->setAttributeMap(attrs.release());
    219     trow->parserAddChild(td);
    220     td->attach();
    221     m_current = m_td = td;
    222 
    223 #ifdef DEBUG_LINE_NUMBERS
    224     RefPtr<Text> lineNumberText = Text::create(this, String::number(parser()->lineNumber() + 1) + " ");
    225     td->addChild(lineNumberText);
    226     lineNumberText->attach();
    227 #endif
    228 
    229     // Open up the needed spans.
    230     if (!className.isEmpty()) {
    231         if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value")
    232             m_current = addSpanWithClassName("webkit-html-tag");
    233         m_current = addSpanWithClassName(className);
    234     }
    235 }
    236 
    237 void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className)
    238 {
    239     if (text.isEmpty())
    240         return;
    241 
    242     // Add in the content, splitting on newlines.
    243     Vector<String> lines;
    244     text.split('\n', true, lines);
    245     unsigned size = lines.size();
    246     for (unsigned i = 0; i < size; i++) {
    247         String substring = lines[i];
    248         if (substring.isEmpty()) {
    249             if (i == size - 1)
    250                 break;
    251             substring = " ";
    252         }
    253         if (m_current == m_tbody)
    254             addLine(className);
    255         RefPtr<Text> t = Text::create(this, substring);
    256         m_current->parserAddChild(t);
    257         t->attach();
    258         if (i < size - 1)
    259             m_current = m_tbody;
    260     }
    261 
    262     // Set current to m_tbody if the last character was a newline.
    263     if (text[text.length() - 1] == '\n')
    264         m_current = m_tbody;
    265 }
    266 
    267 int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor)
    268 {
    269     ASSERT(start <= end);
    270     if (start == end)
    271         return start;
    272 
    273     String text = source.substring(start, end - start);
    274     if (!className.isEmpty()) {
    275         if (isLink)
    276             m_current = addLink(text, isAnchor);
    277         else
    278             m_current = addSpanWithClassName(className);
    279     }
    280     addText(text, className);
    281     if (!className.isEmpty() && m_current != m_tbody)
    282         m_current = static_cast<Element*>(m_current->parentNode());
    283     return end;
    284 }
    285 
    286 PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href)
    287 {
    288     RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, this);
    289     RefPtr<NamedNodeMap> attributeMap = NamedNodeMap::create();
    290     attributeMap->addAttribute(Attribute::createMapped(hrefAttr, href));
    291     base->setAttributeMap(attributeMap.release());
    292     m_current->parserAddChild(base);
    293     base->attach();
    294     return base.release();
    295 }
    296 
    297 PassRefPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor)
    298 {
    299     if (m_current == m_tbody)
    300         addLine("webkit-html-tag");
    301 
    302     // Now create a link for the attribute value instead of a span.
    303     RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(this);
    304     RefPtr<NamedNodeMap> attrs = NamedNodeMap::create();
    305     const char* classValue;
    306     if (isAnchor)
    307         classValue = "webkit-html-attribute-value webkit-html-external-link";
    308     else
    309         classValue = "webkit-html-attribute-value webkit-html-resource-link";
    310     attrs->addAttribute(Attribute::createMapped(classAttr, classValue));
    311     attrs->addAttribute(Attribute::createMapped(targetAttr, "_blank"));
    312     attrs->addAttribute(Attribute::createMapped(hrefAttr, url));
    313     anchor->setAttributeMap(attrs.release());
    314     m_current->parserAddChild(anchor);
    315     anchor->attach();
    316     return anchor.release();
    317 }
    318 
    319 }
    320