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