mirror of
https://github.com/servo/servo
synced 2026-04-26 01:25:32 +02:00
A lot (and I mean, really a lot) depends on these constructors. Therefore, this is the one spaghetti ball that I could extract and convert all `can_gc` to `cx`. There are some new introductions of `temp_cx` in the callbacks of the servo parser, but we already had some in other callbacks. Part of #40600 Testing: It compiles Signed-off-by: Tim van der Lippe <tvanderlippe@gmail.com>
281 lines
10 KiB
Rust
281 lines
10 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||
|
||
use dom_struct::dom_struct;
|
||
use html5ever::{QualName, local_name, ns};
|
||
use js::context::JSContext;
|
||
use script_bindings::error::Error;
|
||
use script_traits::DocumentActivity;
|
||
|
||
use crate::document_loader::DocumentLoader;
|
||
use crate::dom::bindings::codegen::Bindings::DOMImplementationBinding::DOMImplementationMethods;
|
||
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
|
||
DocumentMethods, ElementCreationOptions,
|
||
};
|
||
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
|
||
use crate::dom::bindings::codegen::UnionTypes::StringOrElementCreationOptions;
|
||
use crate::dom::bindings::domname::{is_valid_doctype_name, namespace_from_domstring};
|
||
use crate::dom::bindings::error::Fallible;
|
||
use crate::dom::bindings::inheritance::Castable;
|
||
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object};
|
||
use crate::dom::bindings::root::{Dom, DomRoot};
|
||
use crate::dom::bindings::str::DOMString;
|
||
use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
|
||
use crate::dom::documenttype::DocumentType;
|
||
use crate::dom::element::{CustomElementCreationMode, ElementCreator};
|
||
use crate::dom::node::Node;
|
||
use crate::dom::text::Text;
|
||
use crate::dom::types::Element;
|
||
use crate::dom::xmldocument::XMLDocument;
|
||
use crate::script_runtime::CanGc;
|
||
|
||
// https://dom.spec.whatwg.org/#domimplementation
|
||
#[dom_struct]
|
||
pub(crate) struct DOMImplementation {
|
||
reflector_: Reflector,
|
||
document: Dom<Document>,
|
||
}
|
||
|
||
impl DOMImplementation {
|
||
fn new_inherited(document: &Document) -> DOMImplementation {
|
||
DOMImplementation {
|
||
reflector_: Reflector::new(),
|
||
document: Dom::from_ref(document),
|
||
}
|
||
}
|
||
|
||
pub(crate) fn new(document: &Document, can_gc: CanGc) -> DomRoot<DOMImplementation> {
|
||
let window = document.window();
|
||
reflect_dom_object(
|
||
Box::new(DOMImplementation::new_inherited(document)),
|
||
window,
|
||
can_gc,
|
||
)
|
||
}
|
||
}
|
||
|
||
// https://dom.spec.whatwg.org/#domimplementation
|
||
impl DOMImplementationMethods<crate::DomTypeHolder> for DOMImplementation {
|
||
/// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype>
|
||
fn CreateDocumentType(
|
||
&self,
|
||
cx: &mut js::context::JSContext,
|
||
qualified_name: DOMString,
|
||
pubid: DOMString,
|
||
sysid: DOMString,
|
||
) -> Fallible<DomRoot<DocumentType>> {
|
||
// Step 1. If name is not a valid doctype name, then throw an
|
||
// "InvalidCharacterError" DOMException.
|
||
if !is_valid_doctype_name(&qualified_name) {
|
||
debug!("Not a valid doctype name");
|
||
return Err(Error::InvalidCharacter(None));
|
||
}
|
||
|
||
Ok(DocumentType::new(
|
||
cx,
|
||
qualified_name,
|
||
Some(pubid),
|
||
Some(sysid),
|
||
&self.document,
|
||
))
|
||
}
|
||
|
||
/// <https://dom.spec.whatwg.org/#dom-domimplementation-createdocument>
|
||
fn CreateDocument(
|
||
&self,
|
||
cx: &mut JSContext,
|
||
maybe_namespace: Option<DOMString>,
|
||
qname: DOMString,
|
||
maybe_doctype: Option<&DocumentType>,
|
||
) -> Fallible<DomRoot<XMLDocument>> {
|
||
let win = self.document.window();
|
||
let loader = DocumentLoader::new(&self.document.loader());
|
||
let namespace = namespace_from_domstring(maybe_namespace.to_owned());
|
||
|
||
let content_type = match namespace {
|
||
ns!(html) => "application/xhtml+xml",
|
||
ns!(svg) => "image/svg+xml",
|
||
_ => "application/xml",
|
||
}
|
||
.parse()
|
||
.unwrap();
|
||
|
||
// Step 1. Let document be a new XMLDocument.
|
||
let doc = XMLDocument::new(
|
||
win,
|
||
HasBrowsingContext::No,
|
||
None,
|
||
self.document.origin().clone(),
|
||
IsHTMLDocument::NonHTMLDocument,
|
||
Some(content_type),
|
||
None,
|
||
DocumentActivity::Inactive,
|
||
DocumentSource::NotFromParser,
|
||
loader,
|
||
Some(self.document.insecure_requests_policy()),
|
||
self.document.has_trustworthy_ancestor_or_current_origin(),
|
||
self.document.custom_element_reaction_stack(),
|
||
CanGc::from_cx(cx),
|
||
);
|
||
|
||
// Step 2. Let element be null.
|
||
// Step 3. If qualifiedName is not the empty string, then set element to the result of running
|
||
// the internal createElementNS steps, given document, namespace, qualifiedName, and an empty dictionary.
|
||
let maybe_elem = if qname.is_empty() {
|
||
None
|
||
} else {
|
||
let options =
|
||
StringOrElementCreationOptions::ElementCreationOptions(ElementCreationOptions {
|
||
is: None,
|
||
});
|
||
match doc
|
||
.upcast::<Document>()
|
||
.CreateElementNS(cx, maybe_namespace, qname, options)
|
||
{
|
||
Err(error) => return Err(error),
|
||
Ok(elem) => Some(elem),
|
||
}
|
||
};
|
||
|
||
{
|
||
let doc_node = doc.upcast::<Node>();
|
||
|
||
// Step 4.
|
||
if let Some(doc_type) = maybe_doctype {
|
||
doc_node.AppendChild(cx, doc_type.upcast()).unwrap();
|
||
}
|
||
|
||
// Step 5.
|
||
if let Some(ref elem) = maybe_elem {
|
||
doc_node.AppendChild(cx, elem.upcast()).unwrap();
|
||
}
|
||
}
|
||
|
||
// Step 6.
|
||
// The origin is already set
|
||
|
||
// Step 7.
|
||
Ok(doc)
|
||
}
|
||
|
||
/// <https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument>
|
||
fn CreateHTMLDocument(
|
||
&self,
|
||
cx: &mut JSContext,
|
||
title: Option<DOMString>,
|
||
) -> DomRoot<Document> {
|
||
let win = self.document.window();
|
||
let loader = DocumentLoader::new(&self.document.loader());
|
||
|
||
// Step 1. Let doc be a new document that is an HTML document.
|
||
// Step 2. Set doc’s content type to "text/html".
|
||
let doc = Document::new(
|
||
win,
|
||
HasBrowsingContext::No,
|
||
None,
|
||
None,
|
||
// Step 8. doc’s origin is this’s associated document’s origin.
|
||
self.document.origin().clone(),
|
||
IsHTMLDocument::HTMLDocument,
|
||
None,
|
||
None,
|
||
DocumentActivity::Inactive,
|
||
DocumentSource::NotFromParser,
|
||
loader,
|
||
None,
|
||
None,
|
||
Default::default(),
|
||
false,
|
||
self.document.allow_declarative_shadow_roots(),
|
||
Some(self.document.insecure_requests_policy()),
|
||
self.document.has_trustworthy_ancestor_or_current_origin(),
|
||
self.document.custom_element_reaction_stack(),
|
||
self.document.creation_sandboxing_flag_set(),
|
||
CanGc::from_cx(cx),
|
||
);
|
||
|
||
{
|
||
// Step 3. Append a new doctype, with "html" as its name and with its node document set to doc, to doc.
|
||
let doc_node = doc.upcast::<Node>();
|
||
let doc_type = DocumentType::new(cx, DOMString::from("html"), None, None, &doc);
|
||
doc_node.AppendChild(cx, doc_type.upcast()).unwrap();
|
||
}
|
||
|
||
{
|
||
// Step 4. Append the result of creating an element given doc, "html",
|
||
// and the HTML namespace, to doc.
|
||
let doc_node = doc.upcast::<Node>();
|
||
let doc_html = DomRoot::upcast::<Node>(Element::create(
|
||
cx,
|
||
QualName::new(None, ns!(html), local_name!("html")),
|
||
None,
|
||
&doc,
|
||
ElementCreator::ScriptCreated,
|
||
CustomElementCreationMode::Asynchronous,
|
||
None,
|
||
));
|
||
doc_node
|
||
.AppendChild(cx, &doc_html)
|
||
.expect("Appending failed");
|
||
|
||
{
|
||
// Step 5. Append the result of creating an element given doc, "head",
|
||
// and the HTML namespace, to the html element created earlier.
|
||
let doc_head = DomRoot::upcast::<Node>(Element::create(
|
||
cx,
|
||
QualName::new(None, ns!(html), local_name!("head")),
|
||
None,
|
||
&doc,
|
||
ElementCreator::ScriptCreated,
|
||
CustomElementCreationMode::Asynchronous,
|
||
None,
|
||
));
|
||
doc_html.AppendChild(cx, &doc_head).unwrap();
|
||
|
||
// Step 6. If title is given:
|
||
if let Some(title_str) = title {
|
||
// Step 6.1. Append the result of creating an element given doc, "title",
|
||
// and the HTML namespace, to the head element created earlier.
|
||
let doc_title = DomRoot::upcast::<Node>(Element::create(
|
||
cx,
|
||
QualName::new(None, ns!(html), local_name!("title")),
|
||
None,
|
||
&doc,
|
||
ElementCreator::ScriptCreated,
|
||
CustomElementCreationMode::Asynchronous,
|
||
None,
|
||
));
|
||
doc_head.AppendChild(cx, &doc_title).unwrap();
|
||
|
||
// Step 6.2. Append a new Text node, with its data set to title (which could be the empty string)
|
||
// and its node document set to doc, to the title element created earlier.
|
||
let title_text = Text::new(cx, title_str, &doc);
|
||
doc_title.AppendChild(cx, title_text.upcast()).unwrap();
|
||
}
|
||
}
|
||
|
||
// Step 7. Append the result of creating an element given doc, "body",
|
||
// and the HTML namespace, to the html element created earlier.
|
||
let doc_body = Element::create(
|
||
cx,
|
||
QualName::new(None, ns!(html), local_name!("body")),
|
||
None,
|
||
&doc,
|
||
ElementCreator::ScriptCreated,
|
||
CustomElementCreationMode::Asynchronous,
|
||
None,
|
||
);
|
||
doc_html.AppendChild(cx, doc_body.upcast()).unwrap();
|
||
}
|
||
|
||
// Step 9. Return doc.
|
||
doc
|
||
}
|
||
|
||
/// <https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature>
|
||
fn HasFeature(&self) -> bool {
|
||
true
|
||
}
|
||
}
|