| /* |
| * Copyright (C) 2013 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following disclaimer |
| * in the documentation and/or other materials provided with the |
| * distribution. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/html/imports/html_import_loader.h" |
| |
| #include "third_party/blink/renderer/core/css/style_engine.h" |
| #include "third_party/blink/renderer/core/dom/document.h" |
| #include "third_party/blink/renderer/core/dom/document_init.h" |
| #include "third_party/blink/renderer/core/dom/document_parser.h" |
| #include "third_party/blink/renderer/core/html/html_document.h" |
| #include "third_party/blink/renderer/core/html/imports/html_import_child.h" |
| #include "third_party/blink/renderer/core/html/imports/html_imports_controller.h" |
| #include "third_party/blink/renderer/platform/heap/heap.h" |
| #include "third_party/blink/renderer/platform/network/content_security_policy_response_headers.h" |
| |
| namespace blink { |
| |
| HTMLImportLoader::HTMLImportLoader(HTMLImportsController* controller) |
| : controller_(controller), state_(kStateLoading) {} |
| |
| HTMLImportLoader::~HTMLImportLoader() = default; |
| |
| void HTMLImportLoader::Dispose() { |
| controller_ = nullptr; |
| if (document_) { |
| if (document_->Parser()) |
| document_->Parser()->RemoveClient(this); |
| document_->ClearImportsController(); |
| document_.Clear(); |
| } |
| ClearResource(); |
| } |
| |
| void HTMLImportLoader::ResponseReceived(Resource* resource, |
| const ResourceResponse& response) { |
| // Resource may already have been loaded with the import loader |
| // being added as a client later & now being notified. Fail early. |
| if (resource->LoadFailedOrCanceled() || response.HttpStatusCode() >= 400 || |
| !response.HttpHeaderField(http_names::kContentDisposition).IsNull()) { |
| SetState(kStateError); |
| return; |
| } |
| SetState(StartWritingAndParsing(response)); |
| } |
| |
| void HTMLImportLoader::DataReceived(Resource* resource, |
| const char* data, |
| size_t length) { |
| document_->Parser()->AppendBytes(data, length); |
| } |
| |
| void HTMLImportLoader::NotifyFinished(Resource* resource) { |
| // If part of the document was already loaded, we don't treat the load failure |
| // as an error because the partially-loaded document has been visible from |
| // script at this point. |
| if (resource->LoadFailedOrCanceled() && !document_) { |
| SetState(kStateError); |
| return; |
| } |
| |
| SetState(FinishWriting()); |
| } |
| |
| HTMLImportLoader::State HTMLImportLoader::StartWritingAndParsing( |
| const ResourceResponse& response) { |
| DCHECK(controller_); |
| DCHECK(!imports_.IsEmpty()); |
| Document* tree_root = controller_->TreeRoot(); |
| document_ = MakeGarbageCollected<HTMLDocument>( |
| DocumentInit::Create() |
| .WithImportsController(controller_) |
| .WithExecutionContext(tree_root->GetExecutionContext()) |
| .WithURL(response.CurrentRequestUrl())); |
| document_->OpenForNavigation( |
| RuntimeEnabledFeatures::ForceSynchronousHTMLParsingEnabled() |
| ? kAllowDeferredParsing |
| : kAllowAsynchronousParsing, |
| response.MimeType(), "UTF-8"); |
| |
| DocumentParser* parser = document_->Parser(); |
| DCHECK(parser); |
| parser->AddClient(this); |
| |
| return kStateLoading; |
| } |
| |
| HTMLImportLoader::State HTMLImportLoader::FinishWriting() { |
| return kStateWritten; |
| } |
| |
| HTMLImportLoader::State HTMLImportLoader::FinishParsing() { |
| return kStateParsed; |
| } |
| |
| HTMLImportLoader::State HTMLImportLoader::FinishLoading() { |
| return kStateLoaded; |
| } |
| |
| void HTMLImportLoader::SetState(State state) { |
| if (state_ == state) |
| return; |
| |
| state_ = state; |
| |
| if (state_ == kStateParsed || state_ == kStateError || |
| state_ == kStateWritten) { |
| if (document_) |
| document_->Parser()->Finish(); |
| } |
| |
| // Since DocumentParser::Finish() can let setState() reenter, we shouldn't |
| // refer to state_ here. |
| if (state == kStateLoaded) |
| document_->SetReadyState(Document::kComplete); |
| if (state == kStateLoaded || state == kStateError) |
| DidFinishLoading(); |
| } |
| |
| void HTMLImportLoader::NotifyParserStopped() { |
| SetState(FinishParsing()); |
| if (!HasPendingResources()) |
| SetState(FinishLoading()); |
| |
| DocumentParser* parser = document_->Parser(); |
| DCHECK(parser); |
| parser->RemoveClient(this); |
| } |
| |
| void HTMLImportLoader::DidRemoveAllPendingStylesheets() { |
| if (state_ == kStateParsed) |
| SetState(FinishLoading()); |
| } |
| |
| bool HTMLImportLoader::HasPendingResources() const { |
| return document_ && |
| document_->GetStyleEngine().HasPendingScriptBlockingSheets(); |
| } |
| |
| void HTMLImportLoader::DidFinishLoading() { |
| for (const auto& import_child : imports_) |
| import_child->DidFinishLoading(); |
| |
| ClearResource(); |
| |
| DCHECK(!document_ || !document_->Parsing()); |
| } |
| |
| void HTMLImportLoader::MoveToFirst(HTMLImportChild* import) { |
| wtf_size_t position = imports_.Find(import); |
| DCHECK_NE(kNotFound, position); |
| imports_.EraseAt(position); |
| imports_.insert(0, import); |
| } |
| |
| void HTMLImportLoader::AddImport(HTMLImportChild* import) { |
| DCHECK_EQ(kNotFound, imports_.Find(import)); |
| |
| imports_.push_back(import); |
| import->Normalize(); |
| if (IsDone()) |
| import->DidFinishLoading(); |
| } |
| |
| void HTMLImportLoader::RemoveImport(HTMLImportChild* client) { |
| DCHECK_NE(kNotFound, imports_.Find(client)); |
| imports_.EraseAt(imports_.Find(client)); |
| } |
| |
| bool HTMLImportLoader::ShouldBlockScriptExecution() const { |
| return FirstImport()->GetState().ShouldBlockScriptExecution(); |
| } |
| |
| void HTMLImportLoader::Trace(Visitor* visitor) const { |
| visitor->Trace(controller_); |
| visitor->Trace(imports_); |
| visitor->Trace(document_); |
| DocumentParserClient::Trace(visitor); |
| RawResourceClient::Trace(visitor); |
| } |
| |
| } // namespace blink |