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
HTMLImportLoader(HTMLImportsController * controller)46 HTMLImportLoader::HTMLImportLoader(HTMLImportsController* controller)
47 : m_controller(controller)
48 , m_state(StateLoading)
49 , m_microtaskQueue(CustomElementSyncMicrotaskQueue::create())
50 {
51 }
52
~HTMLImportLoader()53 HTMLImportLoader::~HTMLImportLoader()
54 {
55 #if !ENABLE(OILPAN)
56 clear();
57 #endif
58 }
59
60 #if !ENABLE(OILPAN)
importDestroyed()61 void HTMLImportLoader::importDestroyed()
62 {
63 clear();
64 }
65
clear()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
startLoading(const ResourcePtr<RawResource> & resource)77 void HTMLImportLoader::startLoading(const ResourcePtr<RawResource>& resource)
78 {
79 setResource(resource);
80 }
81
responseReceived(Resource * resource,const ResourceResponse & response)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
dataReceived(Resource *,const char * data,int length)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
notifyFinished(Resource * resource)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
startWritingAndParsing(const ResourceResponse & response)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
finishWriting()122 HTMLImportLoader::State HTMLImportLoader::finishWriting()
123 {
124 return StateWritten;
125 }
126
finishParsing()127 HTMLImportLoader::State HTMLImportLoader::finishParsing()
128 {
129 return StateParsed;
130 }
131
finishLoading()132 HTMLImportLoader::State HTMLImportLoader::finishLoading()
133 {
134 return StateLoaded;
135 }
136
setState(State state)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
didFinishParsing()154 void HTMLImportLoader::didFinishParsing()
155 {
156 setState(finishParsing());
157 if (!hasPendingResources())
158 setState(finishLoading());
159 }
160
didRemoveAllPendingStylesheet()161 void HTMLImportLoader::didRemoveAllPendingStylesheet()
162 {
163 if (m_state == StateParsed)
164 setState(finishLoading());
165 }
166
hasPendingResources() const167 bool HTMLImportLoader::hasPendingResources() const
168 {
169 return m_document && m_document->styleEngine()->hasPendingSheets();
170 }
171
didFinishLoading()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
moveToFirst(HTMLImportChild * import)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
addImport(HTMLImportChild * import)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)
removeImport(HTMLImportChild * client)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
shouldBlockScriptExecution() const208 bool HTMLImportLoader::shouldBlockScriptExecution() const
209 {
210 return firstImport()->state().shouldBlockScriptExecution();
211 }
212
microtaskQueue() const213 PassRefPtrWillBeRawPtr<CustomElementSyncMicrotaskQueue> HTMLImportLoader::microtaskQueue() const
214 {
215 return m_microtaskQueue;
216 }
217
trace(Visitor * visitor)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