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