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