| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "core/xml/DocumentXSLT.h" |
| |
| #include "bindings/core/v8/V8BindingForCore.h" |
| #include "core/dom/Document.h" |
| #include "core/dom/ExecutionContext.h" |
| #include "core/dom/Node.h" |
| #include "core/dom/ProcessingInstruction.h" |
| #include "core/dom/events/Event.h" |
| #include "core/dom/events/EventListener.h" |
| #include "core/frame/UseCounter.h" |
| #include "core/probe/CoreProbes.h" |
| #include "core/xml/XSLStyleSheet.h" |
| #include "core/xml/XSLTProcessor.h" |
| #include "platform/bindings/DOMWrapperWorld.h" |
| #include "platform/bindings/ScriptState.h" |
| |
| namespace blink { |
| |
| class DOMContentLoadedListener final |
| : public EventListener, |
| public ProcessingInstruction::DetachableEventListener { |
| USING_GARBAGE_COLLECTED_MIXIN(DOMContentLoadedListener); |
| |
| public: |
| static DOMContentLoadedListener* Create(ProcessingInstruction* pi) { |
| return new DOMContentLoadedListener(pi); |
| } |
| |
| bool operator==(const EventListener& rhs) const override { |
| return this == &rhs; |
| } |
| |
| void handleEvent(ExecutionContext* execution_context, Event* event) override { |
| DCHECK(RuntimeEnabledFeatures::XSLTEnabled()); |
| DCHECK_EQ(event->type(), "DOMContentLoaded"); |
| |
| Document& document = *ToDocument(execution_context); |
| DCHECK(!document.Parsing()); |
| |
| // Processing instruction (XML documents only). |
| // We don't support linking to embedded CSS stylesheets, |
| // see <https://bugs.webkit.org/show_bug.cgi?id=49281> for discussion. |
| // Don't apply XSL transforms to already transformed documents. |
| if (DocumentXSLT::HasTransformSourceDocument(document)) |
| return; |
| |
| ProcessingInstruction* pi = DocumentXSLT::FindXSLStyleSheet(document); |
| if (!pi || pi != processing_instruction_ || pi->IsLoading()) |
| return; |
| DocumentXSLT::ApplyXSLTransform(document, pi); |
| } |
| |
| void Detach() override { processing_instruction_ = nullptr; } |
| |
| EventListener* ToEventListener() override { return this; } |
| |
| virtual void Trace(blink::Visitor* visitor) { |
| visitor->Trace(processing_instruction_); |
| EventListener::Trace(visitor); |
| ProcessingInstruction::DetachableEventListener::Trace(visitor); |
| } |
| |
| private: |
| DOMContentLoadedListener(ProcessingInstruction* pi) |
| : EventListener(EventListener::kCPPEventListenerType), |
| processing_instruction_(pi) {} |
| |
| // If this event listener is attached to a ProcessingInstruction, keep a |
| // weak reference back to it. That ProcessingInstruction is responsible for |
| // detaching itself and clear out the reference. |
| Member<ProcessingInstruction> processing_instruction_; |
| }; |
| |
| DocumentXSLT::DocumentXSLT(Document& document) |
| : Supplement<Document>(document), transform_source_document_(nullptr) {} |
| |
| void DocumentXSLT::ApplyXSLTransform(Document& document, |
| ProcessingInstruction* pi) { |
| DCHECK(!pi->IsLoading()); |
| UseCounter::Count(document, WebFeature::kXSLProcessingInstruction); |
| XSLTProcessor* processor = XSLTProcessor::Create(document); |
| processor->SetXSLStyleSheet(ToXSLStyleSheet(pi->sheet())); |
| String result_mime_type; |
| String new_source; |
| String result_encoding; |
| document.SetParsingState(Document::kParsing); |
| if (!processor->TransformToString(&document, result_mime_type, new_source, |
| result_encoding)) { |
| document.SetParsingState(Document::kFinishedParsing); |
| return; |
| } |
| // FIXME: If the transform failed we should probably report an error (like |
| // Mozilla does). |
| LocalFrame* owner_frame = document.GetFrame(); |
| processor->CreateDocumentFromSource(new_source, result_encoding, |
| result_mime_type, &document, owner_frame); |
| probe::frameDocumentUpdated(owner_frame); |
| document.SetParsingState(Document::kFinishedParsing); |
| } |
| |
| ProcessingInstruction* DocumentXSLT::FindXSLStyleSheet(Document& document) { |
| for (Node* node = document.firstChild(); node; node = node->nextSibling()) { |
| if (node->getNodeType() != Node::kProcessingInstructionNode) |
| continue; |
| |
| ProcessingInstruction* pi = ToProcessingInstruction(node); |
| if (pi->IsXSL()) |
| return pi; |
| } |
| return nullptr; |
| } |
| |
| bool DocumentXSLT::ProcessingInstructionInsertedIntoDocument( |
| Document& document, |
| ProcessingInstruction* pi) { |
| if (!pi->IsXSL()) |
| return false; |
| |
| if (!RuntimeEnabledFeatures::XSLTEnabled() || !document.GetFrame()) |
| return true; |
| |
| DOMContentLoadedListener* listener = DOMContentLoadedListener::Create(pi); |
| document.addEventListener(EventTypeNames::DOMContentLoaded, listener, false); |
| DCHECK(!pi->EventListenerForXSLT()); |
| pi->SetEventListenerForXSLT(listener); |
| return true; |
| } |
| |
| bool DocumentXSLT::ProcessingInstructionRemovedFromDocument( |
| Document& document, |
| ProcessingInstruction* pi) { |
| if (!pi->IsXSL()) |
| return false; |
| |
| if (!pi->EventListenerForXSLT()) |
| return true; |
| |
| DCHECK(RuntimeEnabledFeatures::XSLTEnabled()); |
| document.removeEventListener(EventTypeNames::DOMContentLoaded, |
| pi->EventListenerForXSLT(), false); |
| pi->ClearEventListenerForXSLT(); |
| return true; |
| } |
| |
| bool DocumentXSLT::SheetLoaded(Document& document, ProcessingInstruction* pi) { |
| if (!pi->IsXSL()) |
| return false; |
| |
| if (RuntimeEnabledFeatures::XSLTEnabled() && !document.Parsing() && |
| !pi->IsLoading() && !DocumentXSLT::HasTransformSourceDocument(document)) { |
| if (FindXSLStyleSheet(document) == pi) |
| ApplyXSLTransform(document, pi); |
| } |
| return true; |
| } |
| |
| const char* DocumentXSLT::SupplementName() { |
| return "DocumentXSLT"; |
| } |
| |
| bool DocumentXSLT::HasTransformSourceDocument(Document& document) { |
| return static_cast<DocumentXSLT*>( |
| Supplement<Document>::From(document, SupplementName())); |
| } |
| |
| DocumentXSLT& DocumentXSLT::From(Document& document) { |
| DocumentXSLT* supplement = static_cast<DocumentXSLT*>( |
| Supplement<Document>::From(document, SupplementName())); |
| if (!supplement) { |
| supplement = new DocumentXSLT(document); |
| Supplement<Document>::ProvideTo(document, SupplementName(), supplement); |
| } |
| return *supplement; |
| } |
| |
| void DocumentXSLT::Trace(blink::Visitor* visitor) { |
| visitor->Trace(transform_source_document_); |
| Supplement<Document>::Trace(visitor); |
| } |
| |
| } // namespace blink |