Home | History | Annotate | Download | only in html
      1 /*
      2  * Copyright (C) 2013 Google 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 are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "core/html/HTMLImportLoader.h"
     33 
     34 #include "core/dom/Document.h"
     35 #include "core/html/HTMLDocument.h"
     36 #include "core/html/HTMLImportLoaderClient.h"
     37 #include "core/loader/DocumentWriter.h"
     38 #include "core/loader/cache/ResourceFetcher.h"
     39 #include "core/page/ContentSecurityPolicyResponseHeaders.h"
     40 
     41 namespace WebCore {
     42 
     43 HTMLImportLoader::HTMLImportLoader(HTMLImport* parent, const KURL& url, const ResourcePtr<RawResource>& resource)
     44     : m_parent(parent)
     45     , m_state(StateLoading)
     46     , m_resource(resource)
     47     , m_url(url)
     48 {
     49     m_resource->addClient(this);
     50 }
     51 
     52 HTMLImportLoader::~HTMLImportLoader()
     53 {
     54     // importDestroyed() should be called before the destruction.
     55     ASSERT(!m_parent);
     56     ASSERT(!m_importedDocument);
     57     if (m_resource)
     58         m_resource->removeClient(this);
     59 }
     60 
     61 void HTMLImportLoader::responseReceived(Resource*, const ResourceResponse& response)
     62 {
     63     setState(startWritingAndParsing(response));
     64 }
     65 
     66 void HTMLImportLoader::dataReceived(Resource*, const char* data, int length)
     67 {
     68     RefPtr<DocumentWriter> protectingWriter(m_writer);
     69     m_writer->addData(data, length);
     70 }
     71 
     72 void HTMLImportLoader::notifyFinished(Resource*)
     73 {
     74     setState(finishWriting());
     75 }
     76 
     77 void HTMLImportLoader::setState(State state)
     78 {
     79     if (m_state == state)
     80         return;
     81 
     82     m_state = state;
     83 
     84     if (m_state == StateReady || m_state == StateError || m_state == StateWritten) {
     85         if (RefPtr<DocumentWriter> writer = m_writer.release())
     86             writer->end();
     87     }
     88 
     89     // Since DocumentWriter::end() let setState() reenter, we shouldn't refer to m_state here.
     90     if (state == StateReady || state == StateError)
     91         didFinish();
     92 }
     93 
     94 void HTMLImportLoader::didFinish()
     95 {
     96     for (size_t i = 0; i < m_clients.size(); ++i)
     97         m_clients[i]->didFinish();
     98 
     99     if (m_resource) {
    100         m_resource->removeClient(this);
    101         m_resource = 0;
    102     }
    103 
    104     ASSERT(!document() || !document()->parsing());
    105     root()->importWasDisposed();
    106 }
    107 
    108 HTMLImportLoader::State HTMLImportLoader::startWritingAndParsing(const ResourceResponse& response)
    109 {
    110     // Current canAccess() implementation isn't sufficient for catching cross-domain redirects: http://crbug.com/256976
    111     if (!m_parent->document()->fetcher()->canAccess(m_resource.get()))
    112         return StateError;
    113 
    114     m_importedDocument = HTMLDocument::create(DocumentInit(response.url(), 0, this).withRegistrationContext(root()->document()->registrationContext()));
    115     m_importedDocument->initContentSecurityPolicy(ContentSecurityPolicyResponseHeaders(response));
    116     m_writer = DocumentWriter::create(m_importedDocument.get(), response.mimeType(), response.textEncodingName());
    117 
    118     return StateLoading;
    119 }
    120 
    121 HTMLImportLoader::State HTMLImportLoader::finishWriting()
    122 {
    123     if (!m_parent)
    124         return StateError;
    125     // The writer instance indicates that a part of the document can be already loaded.
    126     // We don't take such a case as an error because the partially-loaded document has been visible from script at this point.
    127     if (m_resource->loadFailedOrCanceled() && !m_writer)
    128         return StateError;
    129 
    130     return StateWritten;
    131 }
    132 
    133 HTMLImportLoader::State HTMLImportLoader::finishParsing()
    134 {
    135     if (!m_parent)
    136         return StateError;
    137     return StateReady;
    138 }
    139 
    140 Document* HTMLImportLoader::importedDocument() const
    141 {
    142     if (m_state == StateError)
    143         return 0;
    144     return m_importedDocument.get();
    145 }
    146 
    147 void HTMLImportLoader::addClient(HTMLImportLoaderClient* client)
    148 {
    149     ASSERT(notFound == m_clients.find(client));
    150     m_clients.append(client);
    151     if (isDone())
    152         client->didFinish();
    153 }
    154 
    155 void HTMLImportLoader::removeClient(HTMLImportLoaderClient* client)
    156 {
    157     ASSERT(notFound != m_clients.find(client));
    158     m_clients.remove(m_clients.find(client));
    159 }
    160 
    161 void HTMLImportLoader::importDestroyed()
    162 {
    163     m_parent = 0;
    164     if (RefPtr<Document> document = m_importedDocument.release())
    165         document->setImport(0);
    166 }
    167 
    168 HTMLImportRoot* HTMLImportLoader::root()
    169 {
    170     return m_parent ? m_parent->root() : 0;
    171 }
    172 
    173 HTMLImport* HTMLImportLoader::parent() const
    174 {
    175     return m_parent;
    176 }
    177 
    178 Document* HTMLImportLoader::document() const
    179 {
    180     return m_importedDocument.get();
    181 }
    182 
    183 void HTMLImportLoader::wasDetachedFromDocument()
    184 {
    185     // For imported documens this shouldn't be called because Document::m_import is
    186     // cleared before Document is destroyed by HTMLImportLoader::importDestroyed().
    187     ASSERT_NOT_REACHED();
    188 }
    189 
    190 void HTMLImportLoader::didFinishParsing()
    191 {
    192     setState(finishParsing());
    193 }
    194 
    195 bool HTMLImportLoader::isProcessing() const
    196 {
    197     if (!m_importedDocument)
    198         return !isDone();
    199     return m_importedDocument->parsing();
    200 }
    201 
    202 } // namespace WebCore
    203