Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2000 Peter Kelly (pmk (at) post.com)
      3  * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved.
      4  *
      5  * This library is free software; you can redistribute it and/or
      6  * modify it under the terms of the GNU Library General Public
      7  * License as published by the Free Software Foundation; either
      8  * version 2 of the License, or (at your option) any later version.
      9  *
     10  * This library is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13  * Library General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU Library General Public License
     16  * along with this library; see the file COPYING.LIB.  If not, write to
     17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18  * Boston, MA 02110-1301, USA.
     19  */
     20 
     21 #include "config.h"
     22 #include "ProcessingInstruction.h"
     23 
     24 #include "CSSStyleSheet.h"
     25 #include "CachedCSSStyleSheet.h"
     26 #include "CachedXSLStyleSheet.h"
     27 #include "Document.h"
     28 #include "CachedResourceLoader.h"
     29 #include "ExceptionCode.h"
     30 #include "Frame.h"
     31 #include "FrameLoader.h"
     32 #include "XSLStyleSheet.h"
     33 #include "XMLDocumentParser.h" // for parseAttributes()
     34 #include "MediaList.h"
     35 
     36 namespace WebCore {
     37 
     38 inline ProcessingInstruction::ProcessingInstruction(Document* document, const String& target, const String& data)
     39     : ContainerNode(document)
     40     , m_target(target)
     41     , m_data(data)
     42     , m_cachedSheet(0)
     43     , m_loading(false)
     44     , m_alternate(false)
     45     , m_createdByParser(false)
     46     , m_isCSS(false)
     47 #if ENABLE(XSLT)
     48     , m_isXSL(false)
     49 #endif
     50 {
     51 }
     52 
     53 PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data)
     54 {
     55     return adoptRef(new ProcessingInstruction(document, target, data));
     56 }
     57 
     58 ProcessingInstruction::~ProcessingInstruction()
     59 {
     60     if (m_sheet)
     61         m_sheet->clearOwnerNode();
     62 
     63     if (m_cachedSheet)
     64         m_cachedSheet->removeClient(this);
     65 }
     66 
     67 void ProcessingInstruction::setData(const String& data, ExceptionCode&)
     68 {
     69     int oldLength = m_data.length();
     70     m_data = data;
     71     document()->textRemoved(this, 0, oldLength);
     72     checkStyleSheet();
     73 }
     74 
     75 String ProcessingInstruction::nodeName() const
     76 {
     77     return m_target;
     78 }
     79 
     80 Node::NodeType ProcessingInstruction::nodeType() const
     81 {
     82     return PROCESSING_INSTRUCTION_NODE;
     83 }
     84 
     85 String ProcessingInstruction::nodeValue() const
     86 {
     87     return m_data;
     88 }
     89 
     90 void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec)
     91 {
     92     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
     93     setData(nodeValue, ec);
     94 }
     95 
     96 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/)
     97 {
     98     // FIXME: Is it a problem that this does not copy m_localHref?
     99     // What about other data members?
    100     return create(document(), m_target, m_data);
    101 }
    102 
    103 // DOM Section 1.1.1
    104 bool ProcessingInstruction::childTypeAllowed(NodeType) const
    105 {
    106     return false;
    107 }
    108 
    109 void ProcessingInstruction::checkStyleSheet()
    110 {
    111     if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) {
    112         // see http://www.w3.org/TR/xml-stylesheet/
    113         // ### support stylesheet included in a fragment of this (or another) document
    114         // ### make sure this gets called when adding from javascript
    115         bool attrsOk;
    116         const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk);
    117         if (!attrsOk)
    118             return;
    119         HashMap<String, String>::const_iterator i = attrs.find("type");
    120         String type;
    121         if (i != attrs.end())
    122             type = i->second;
    123 
    124         m_isCSS = type.isEmpty() || type == "text/css";
    125 #if ENABLE(XSLT)
    126         m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
    127                    type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
    128         if (!m_isCSS && !m_isXSL)
    129 #else
    130         if (!m_isCSS)
    131 #endif
    132             return;
    133 
    134         String href = attrs.get("href");
    135         String alternate = attrs.get("alternate");
    136         m_alternate = alternate == "yes";
    137         m_title = attrs.get("title");
    138         m_media = attrs.get("media");
    139 
    140         if (href.length() > 1 && href[0] == '#') {
    141             m_localHref = href.substring(1);
    142 #if ENABLE(XSLT)
    143             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
    144             // to kick off import/include loads that can hang off some parent sheet.
    145             if (m_isXSL) {
    146                 KURL finalURL(ParsedURLString, m_localHref);
    147                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
    148                 m_loading = false;
    149             }
    150 #endif
    151         } else {
    152             if (m_cachedSheet) {
    153                 m_cachedSheet->removeClient(this);
    154                 m_cachedSheet = 0;
    155             }
    156 
    157             String url = document()->completeURL(href).string();
    158             if (!dispatchBeforeLoadEvent(url))
    159                 return;
    160 
    161             m_loading = true;
    162             document()->addPendingSheet();
    163 
    164 #if ENABLE(XSLT)
    165             if (m_isXSL)
    166                 m_cachedSheet = document()->cachedResourceLoader()->requestXSLStyleSheet(url);
    167             else
    168 #endif
    169             {
    170                 String charset = attrs.get("charset");
    171                 if (charset.isEmpty())
    172                     charset = document()->charset();
    173 
    174                 m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(url, charset);
    175             }
    176             if (m_cachedSheet)
    177                 m_cachedSheet->addClient(this);
    178             else {
    179                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
    180                 m_loading = false;
    181                 document()->removePendingSheet();
    182             }
    183         }
    184     }
    185 }
    186 
    187 bool ProcessingInstruction::isLoading() const
    188 {
    189     if (m_loading)
    190         return true;
    191     if (!m_sheet)
    192         return false;
    193     return m_sheet->isLoading();
    194 }
    195 
    196 bool ProcessingInstruction::sheetLoaded()
    197 {
    198     if (!isLoading()) {
    199         document()->removePendingSheet();
    200         return true;
    201     }
    202     return false;
    203 }
    204 
    205 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
    206 {
    207     if (!inDocument()) {
    208         ASSERT(!m_sheet);
    209         return;
    210     }
    211 
    212     ASSERT(m_isCSS);
    213     RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset);
    214     m_sheet = newSheet;
    215     // We don't need the cross-origin security check here because we are
    216     // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
    217     // type.
    218     parseStyleSheet(sheet->sheetText(true));
    219     newSheet->setTitle(m_title);
    220     newSheet->setMedia(MediaList::create(newSheet.get(), m_media));
    221     newSheet->setDisabled(m_alternate);
    222 }
    223 
    224 #if ENABLE(XSLT)
    225 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet)
    226 {
    227     ASSERT(m_isXSL);
    228     m_sheet = XSLStyleSheet::create(this, href, baseURL);
    229     parseStyleSheet(sheet);
    230 }
    231 #endif
    232 
    233 void ProcessingInstruction::parseStyleSheet(const String& sheet)
    234 {
    235     m_sheet->parseString(sheet, true);
    236     if (m_cachedSheet)
    237         m_cachedSheet->removeClient(this);
    238     m_cachedSheet = 0;
    239 
    240     m_loading = false;
    241     m_sheet->checkLoaded();
    242 }
    243 
    244 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet)
    245 {
    246     ASSERT(!m_cachedSheet);
    247     ASSERT(!m_loading);
    248     m_sheet = sheet;
    249     m_sheet->setTitle(m_title);
    250     m_sheet->setDisabled(m_alternate);
    251 }
    252 
    253 bool ProcessingInstruction::offsetInCharacters() const
    254 {
    255     return true;
    256 }
    257 
    258 int ProcessingInstruction::maxCharacterOffset() const
    259 {
    260     return static_cast<int>(m_data.length());
    261 }
    262 
    263 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    264 {
    265     if (!sheet())
    266         return;
    267 
    268     addSubresourceURL(urls, sheet()->baseURL());
    269 }
    270 
    271 void ProcessingInstruction::insertedIntoDocument()
    272 {
    273     ContainerNode::insertedIntoDocument();
    274     document()->addStyleSheetCandidateNode(this, m_createdByParser);
    275     checkStyleSheet();
    276 }
    277 
    278 void ProcessingInstruction::removedFromDocument()
    279 {
    280     ContainerNode::removedFromDocument();
    281 
    282     document()->removeStyleSheetCandidateNode(this);
    283 
    284     if (m_sheet) {
    285         ASSERT(m_sheet->ownerNode() == this);
    286         m_sheet->clearOwnerNode();
    287         m_sheet = 0;
    288     }
    289 
    290     if (m_cachedSheet)
    291         document()->styleSelectorChanged(DeferRecalcStyle);
    292 }
    293 
    294 void ProcessingInstruction::finishParsingChildren()
    295 {
    296     m_createdByParser = false;
    297     ContainerNode::finishParsingChildren();
    298 }
    299 
    300 } // namespace
    301