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 "DocLoader.h"
     29 #include "ExceptionCode.h"
     30 #include "Frame.h"
     31 #include "FrameLoader.h"
     32 #include "XSLStyleSheet.h"
     33 #include "XMLTokenizer.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 #if ENABLE(XSLT)
     47     , m_isXSL(false)
     48 #endif
     49 {
     50 }
     51 
     52 PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data)
     53 {
     54     return adoptRef(new ProcessingInstruction(document, target, data));
     55 }
     56 
     57 ProcessingInstruction::~ProcessingInstruction()
     58 {
     59     if (m_cachedSheet)
     60         m_cachedSheet->removeClient(this);
     61 }
     62 
     63 void ProcessingInstruction::setData(const String& data, ExceptionCode&)
     64 {
     65     int oldLength = m_data.length();
     66     m_data = data;
     67     document()->textRemoved(this, 0, oldLength);
     68     checkStyleSheet();
     69 }
     70 
     71 String ProcessingInstruction::nodeName() const
     72 {
     73     return m_target;
     74 }
     75 
     76 Node::NodeType ProcessingInstruction::nodeType() const
     77 {
     78     return PROCESSING_INSTRUCTION_NODE;
     79 }
     80 
     81 String ProcessingInstruction::nodeValue() const
     82 {
     83     return m_data;
     84 }
     85 
     86 void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec)
     87 {
     88     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
     89     setData(nodeValue, ec);
     90 }
     91 
     92 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/)
     93 {
     94     // FIXME: Is it a problem that this does not copy m_localHref?
     95     // What about other data members?
     96     return create(document(), m_target, m_data);
     97 }
     98 
     99 // DOM Section 1.1.1
    100 bool ProcessingInstruction::childTypeAllowed(NodeType)
    101 {
    102     return false;
    103 }
    104 
    105 void ProcessingInstruction::checkStyleSheet()
    106 {
    107     if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) {
    108         // see http://www.w3.org/TR/xml-stylesheet/
    109         // ### support stylesheet included in a fragment of this (or another) document
    110         // ### make sure this gets called when adding from javascript
    111         bool attrsOk;
    112         const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk);
    113         if (!attrsOk)
    114             return;
    115         HashMap<String, String>::const_iterator i = attrs.find("type");
    116         String type;
    117         if (i != attrs.end())
    118             type = i->second;
    119 
    120         bool isCSS = type.isEmpty() || type == "text/css";
    121 #if ENABLE(XSLT)
    122         m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
    123                    type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
    124         if (!isCSS && !m_isXSL)
    125 #else
    126         if (!isCSS)
    127 #endif
    128             return;
    129 
    130         String href = attrs.get("href");
    131         String alternate = attrs.get("alternate");
    132         m_alternate = alternate == "yes";
    133         m_title = attrs.get("title");
    134         m_media = attrs.get("media");
    135 
    136         if (href.length() > 1 && href[0] == '#') {
    137             m_localHref = href.substring(1);
    138 #if ENABLE(XSLT)
    139             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
    140             // to kick off import/include loads that can hang off some parent sheet.
    141             if (m_isXSL) {
    142                 KURL finalURL(ParsedURLString, m_localHref);
    143                 m_sheet = XSLStyleSheet::createInline(this, finalURL);
    144                 m_loading = false;
    145             }
    146 #endif
    147         } else {
    148             if (m_cachedSheet) {
    149                 m_cachedSheet->removeClient(this);
    150                 m_cachedSheet = 0;
    151             }
    152 
    153             String url = document()->completeURL(href).string();
    154             if (!dispatchBeforeLoadEvent(url))
    155                 return;
    156 
    157             m_loading = true;
    158             document()->addPendingSheet();
    159 
    160 #if ENABLE(XSLT)
    161             if (m_isXSL)
    162                 m_cachedSheet = document()->docLoader()->requestXSLStyleSheet(url);
    163             else
    164 #endif
    165             {
    166                 String charset = attrs.get("charset");
    167                 if (charset.isEmpty())
    168                     charset = document()->frame()->loader()->encoding();
    169 
    170                 m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(url, charset);
    171             }
    172             if (m_cachedSheet)
    173                 m_cachedSheet->addClient(this);
    174             else {
    175                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
    176                 m_loading = false;
    177                 document()->removePendingSheet();
    178             }
    179         }
    180     }
    181 }
    182 
    183 bool ProcessingInstruction::isLoading() const
    184 {
    185     if (m_loading)
    186         return true;
    187     if (!m_sheet)
    188         return false;
    189     return m_sheet->isLoading();
    190 }
    191 
    192 bool ProcessingInstruction::sheetLoaded()
    193 {
    194     if (!isLoading()) {
    195         document()->removePendingSheet();
    196         return true;
    197     }
    198     return false;
    199 }
    200 
    201 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
    202 {
    203 #if ENABLE(XSLT)
    204     ASSERT(!m_isXSL);
    205 #endif
    206     RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset);
    207     m_sheet = newSheet;
    208     // We don't need the cross-origin security check here because we are
    209     // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
    210     // type.
    211     parseStyleSheet(sheet->sheetText(true));
    212     newSheet->setTitle(m_title);
    213     newSheet->setMedia(MediaList::create(newSheet.get(), m_media));
    214     newSheet->setDisabled(m_alternate);
    215 }
    216 
    217 #if ENABLE(XSLT)
    218 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet)
    219 {
    220     ASSERT(m_isXSL);
    221     m_sheet = XSLStyleSheet::create(this, href, baseURL);
    222     parseStyleSheet(sheet);
    223 }
    224 #endif
    225 
    226 void ProcessingInstruction::parseStyleSheet(const String& sheet)
    227 {
    228     m_sheet->parseString(sheet, true);
    229     if (m_cachedSheet)
    230         m_cachedSheet->removeClient(this);
    231     m_cachedSheet = 0;
    232 
    233     m_loading = false;
    234     m_sheet->checkLoaded();
    235 }
    236 
    237 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet)
    238 {
    239     ASSERT(!m_cachedSheet);
    240     ASSERT(!m_loading);
    241     m_sheet = sheet;
    242     m_sheet->setTitle(m_title);
    243     m_sheet->setDisabled(m_alternate);
    244 }
    245 
    246 bool ProcessingInstruction::offsetInCharacters() const
    247 {
    248     return true;
    249 }
    250 
    251 int ProcessingInstruction::maxCharacterOffset() const
    252 {
    253     return static_cast<int>(m_data.length());
    254 }
    255 
    256 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    257 {
    258     if (!sheet())
    259         return;
    260 
    261     addSubresourceURL(urls, sheet()->baseURL());
    262 }
    263 
    264 void ProcessingInstruction::insertedIntoDocument()
    265 {
    266     ContainerNode::insertedIntoDocument();
    267     document()->addStyleSheetCandidateNode(this, m_createdByParser);
    268     checkStyleSheet();
    269 }
    270 
    271 void ProcessingInstruction::removedFromDocument()
    272 {
    273     ContainerNode::removedFromDocument();
    274 
    275     document()->removeStyleSheetCandidateNode(this);
    276 
    277     // FIXME: It's terrible to do a synchronous update of the style selector just because a <style> or <link> element got removed.
    278     if (m_cachedSheet)
    279         document()->updateStyleSelector();
    280 }
    281 
    282 void ProcessingInstruction::finishParsingChildren()
    283 {
    284     m_createdByParser = false;
    285     ContainerNode::finishParsingChildren();
    286 }
    287 
    288 #ifdef ANDROID_INSTRUMENT
    289 void* ProcessingInstruction::operator new(size_t size)
    290 {
    291     return Node::operator new(size);
    292 }
    293 
    294 void* ProcessingInstruction::operator new[](size_t size)
    295 {
    296     return Node::operator new[](size);
    297 }
    298 
    299 void ProcessingInstruction::operator delete(void* p, size_t size)
    300 {
    301     Node::operator delete(p, size);
    302 }
    303 
    304 void ProcessingInstruction::operator delete[](void* p, size_t size)
    305 {
    306     Node::operator delete[](p, size);
    307 }
    308 #endif
    309 
    310 } // namespace
    311