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 45 namespace blink { 46 47 HTMLImportLoader::HTMLImportLoader(HTMLImportsController* controller) 48 : m_controller(controller) 49 , m_state(StateLoading) 50 , m_microtaskQueue(CustomElementSyncMicrotaskQueue::create()) 51 { 52 } 53 54 HTMLImportLoader::~HTMLImportLoader() 55 { 56 #if !ENABLE(OILPAN) 57 clear(); 58 #endif 59 } 60 61 #if !ENABLE(OILPAN) 62 void HTMLImportLoader::importDestroyed() 63 { 64 clear(); 65 } 66 67 void 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 78 void HTMLImportLoader::startLoading(const ResourcePtr<RawResource>& resource) 79 { 80 setResource(resource); 81 } 82 83 void 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 94 void HTMLImportLoader::dataReceived(Resource*, const char* data, int length) 95 { 96 RefPtrWillBeRawPtr<DocumentWriter> protectingWriter(m_writer.get()); 97 m_writer->addData(data, length); 98 } 99 100 void 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 112 HTMLImportLoader::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 127 HTMLImportLoader::State HTMLImportLoader::finishWriting() 128 { 129 return StateWritten; 130 } 131 132 HTMLImportLoader::State HTMLImportLoader::finishParsing() 133 { 134 return StateParsed; 135 } 136 137 HTMLImportLoader::State HTMLImportLoader::finishLoading() 138 { 139 return StateLoaded; 140 } 141 142 void 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 159 void 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 170 void HTMLImportLoader::didRemoveAllPendingStylesheet() 171 { 172 if (m_state == StateParsed) 173 setState(finishLoading()); 174 } 175 176 bool HTMLImportLoader::hasPendingResources() const 177 { 178 return m_document && m_document->styleEngine()->hasPendingSheets(); 179 } 180 181 void 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 191 void 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 199 void 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) 210 void HTMLImportLoader::removeImport(HTMLImportChild* client) 211 { 212 ASSERT(kNotFound != m_imports.find(client)); 213 m_imports.remove(m_imports.find(client)); 214 } 215 #endif 216 217 bool HTMLImportLoader::shouldBlockScriptExecution() const 218 { 219 return firstImport()->state().shouldBlockScriptExecution(); 220 } 221 222 PassRefPtrWillBeRawPtr<CustomElementSyncMicrotaskQueue> HTMLImportLoader::microtaskQueue() const 223 { 224 return m_microtaskQueue; 225 } 226 227 void 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