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/imports/HTMLImportLoader.h" 33 34#include "core/dom/Document.h" 35#include "core/dom/DocumentParser.h" 36#include "core/dom/StyleEngine.h" 37#include "core/dom/custom/CustomElementSyncMicrotaskQueue.h" 38#include "core/html/HTMLDocument.h" 39#include "core/html/imports/HTMLImportChild.h" 40#include "core/html/imports/HTMLImportsController.h" 41#include "core/loader/DocumentWriter.h" 42#include "platform/network/ContentSecurityPolicyResponseHeaders.h" 43 44 45namespace blink { 46 47HTMLImportLoader::HTMLImportLoader(HTMLImportsController* controller) 48 : m_controller(controller) 49 , m_state(StateLoading) 50 , m_microtaskQueue(CustomElementSyncMicrotaskQueue::create()) 51{ 52} 53 54HTMLImportLoader::~HTMLImportLoader() 55{ 56#if !ENABLE(OILPAN) 57 clear(); 58#endif 59} 60 61#if !ENABLE(OILPAN) 62void HTMLImportLoader::importDestroyed() 63{ 64 clear(); 65} 66 67void HTMLImportLoader::clear() 68{ 69 m_controller = nullptr; 70 if (m_document) { 71 m_document->setImportsController(0); 72 m_document->cancelParsing(); 73 m_document.clear(); 74 } 75} 76#endif 77 78void HTMLImportLoader::startLoading(const ResourcePtr<RawResource>& resource) 79{ 80 setResource(resource); 81} 82 83void HTMLImportLoader::responseReceived(Resource* resource, const ResourceResponse& response) 84{ 85 // Resource may already have been loaded with the import loader 86 // being added as a client later & now being notified. Fail early. 87 if (resource->loadFailedOrCanceled() || response.httpStatusCode() >= 400) { 88 setState(StateError); 89 return; 90 } 91 setState(startWritingAndParsing(response)); 92} 93 94void HTMLImportLoader::dataReceived(Resource*, const char* data, int length) 95{ 96 RefPtrWillBeRawPtr<DocumentWriter> protectingWriter(m_writer.get()); 97 m_writer->addData(data, length); 98} 99 100void HTMLImportLoader::notifyFinished(Resource* resource) 101{ 102 // The writer instance indicates that a part of the document can be already loaded. 103 // We don't take such a case as an error because the partially-loaded document has been visible from script at this point. 104 if (resource->loadFailedOrCanceled() && !m_writer) { 105 setState(StateError); 106 return; 107 } 108 109 setState(finishWriting()); 110} 111 112HTMLImportLoader::State HTMLImportLoader::startWritingAndParsing(const ResourceResponse& response) 113{ 114 ASSERT(!m_imports.isEmpty()); 115 DocumentInit init = DocumentInit(response.url(), 0, m_controller->master()->contextDocument(), m_controller) 116 .withRegistrationContext(m_controller->master()->registrationContext()); 117 m_document = HTMLDocument::create(init); 118 m_writer = DocumentWriter::create(m_document.get(), response.mimeType(), "UTF-8"); 119 120 DocumentParser* parser = m_document->parser(); 121 ASSERT(parser); 122 parser->addClient(this); 123 124 return StateLoading; 125} 126 127HTMLImportLoader::State HTMLImportLoader::finishWriting() 128{ 129 return StateWritten; 130} 131 132HTMLImportLoader::State HTMLImportLoader::finishParsing() 133{ 134 return StateParsed; 135} 136 137HTMLImportLoader::State HTMLImportLoader::finishLoading() 138{ 139 return StateLoaded; 140} 141 142void HTMLImportLoader::setState(State state) 143{ 144 if (m_state == state) 145 return; 146 147 m_state = state; 148 149 if (m_state == StateParsed || m_state == StateError || m_state == StateWritten) { 150 if (RefPtrWillBeRawPtr<DocumentWriter> writer = m_writer.release()) 151 writer->end(); 152 } 153 154 // Since DocumentWriter::end() can let setState() reenter, we shouldn't refer to m_state here. 155 if (state == StateLoaded || state == StateError) 156 didFinishLoading(); 157} 158 159void HTMLImportLoader::notifyParserStopped() 160{ 161 setState(finishParsing()); 162 if (!hasPendingResources()) 163 setState(finishLoading()); 164 165 DocumentParser* parser = m_document->parser(); 166 ASSERT(parser); 167 parser->removeClient(this); 168} 169 170void HTMLImportLoader::didRemoveAllPendingStylesheet() 171{ 172 if (m_state == StateParsed) 173 setState(finishLoading()); 174} 175 176bool HTMLImportLoader::hasPendingResources() const 177{ 178 return m_document && m_document->styleEngine()->hasPendingSheets(); 179} 180 181void HTMLImportLoader::didFinishLoading() 182{ 183 for (size_t i = 0; i < m_imports.size(); ++i) 184 m_imports[i]->didFinishLoading(); 185 186 clearResource(); 187 188 ASSERT(!m_document || !m_document->parsing()); 189} 190 191void HTMLImportLoader::moveToFirst(HTMLImportChild* import) 192{ 193 size_t position = m_imports.find(import); 194 ASSERT(kNotFound != position); 195 m_imports.remove(position); 196 m_imports.insert(0, import); 197} 198 199void HTMLImportLoader::addImport(HTMLImportChild* import) 200{ 201 ASSERT(kNotFound == m_imports.find(import)); 202 203 m_imports.append(import); 204 import->normalize(); 205 if (isDone()) 206 import->didFinishLoading(); 207} 208 209#if !ENABLE(OILPAN) 210void HTMLImportLoader::removeImport(HTMLImportChild* client) 211{ 212 ASSERT(kNotFound != m_imports.find(client)); 213 m_imports.remove(m_imports.find(client)); 214} 215#endif 216 217bool HTMLImportLoader::shouldBlockScriptExecution() const 218{ 219 return firstImport()->state().shouldBlockScriptExecution(); 220} 221 222PassRefPtrWillBeRawPtr<CustomElementSyncMicrotaskQueue> HTMLImportLoader::microtaskQueue() const 223{ 224 return m_microtaskQueue; 225} 226 227void HTMLImportLoader::trace(Visitor* visitor) 228{ 229 visitor->trace(m_controller); 230#if ENABLE(OILPAN) 231 visitor->trace(m_imports); 232#endif 233 visitor->trace(m_document); 234 visitor->trace(m_writer); 235 visitor->trace(m_microtaskQueue); 236} 237 238} // namespace blink 239