HTMLMetaElement.cpp (6389B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/HTMLMetaElement.h" 8 9 #include "mozilla/AsyncEventDispatcher.h" 10 #include "mozilla/Logging.h" 11 #include "mozilla/StaticPrefs_security.h" 12 #include "mozilla/dom/BindContext.h" 13 #include "mozilla/dom/HTMLMetaElementBinding.h" 14 #include "mozilla/dom/ViewportMetaData.h" 15 #include "mozilla/dom/nsCSPService.h" 16 #include "mozilla/dom/nsCSPUtils.h" 17 #include "nsContentUtils.h" 18 #include "nsIXMLContentSink.h" 19 #include "nsSandboxFlags.h" 20 #include "nsStyleConsts.h" 21 22 static mozilla::LazyLogModule gMetaElementLog("nsMetaElement"); 23 #define LOG(msg) MOZ_LOG(gMetaElementLog, mozilla::LogLevel::Debug, msg) 24 #define LOG_ENABLED() MOZ_LOG_TEST(gMetaElementLog, mozilla::LogLevel::Debug) 25 26 NS_IMPL_NS_NEW_HTML_ELEMENT(Meta) 27 28 namespace mozilla::dom { 29 30 HTMLMetaElement::HTMLMetaElement( 31 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo) 32 : nsGenericHTMLElement(std::move(aNodeInfo)) {} 33 34 HTMLMetaElement::~HTMLMetaElement() = default; 35 36 NS_IMPL_ELEMENT_CLONE(HTMLMetaElement) 37 38 void HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName, 39 const nsAttrValue* aValue, 40 const nsAttrValue* aOldValue, 41 nsIPrincipal* aSubjectPrincipal, 42 bool aNotify) { 43 if (aNameSpaceID == kNameSpaceID_None) { 44 if (Document* document = GetUncomposedDoc()) { 45 if (aName == nsGkAtoms::content) { 46 if (const nsAttrValue* name = GetParsedAttr(nsGkAtoms::name)) { 47 MetaAddedOrChanged(*document, *name, ChangeKind::ContentChange); 48 } 49 CreateAndDispatchEvent(*document, u"DOMMetaChanged"_ns); 50 } else if (aName == nsGkAtoms::name) { 51 if (aOldValue) { 52 MetaRemoved(*document, *aOldValue, ChangeKind::NameChange); 53 } 54 if (aValue) { 55 MetaAddedOrChanged(*document, *aValue, ChangeKind::NameChange); 56 } 57 CreateAndDispatchEvent(*document, u"DOMMetaChanged"_ns); 58 } 59 } 60 } 61 62 return nsGenericHTMLElement::AfterSetAttr( 63 aNameSpaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); 64 } 65 66 nsresult HTMLMetaElement::BindToTree(BindContext& aContext, nsINode& aParent) { 67 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent); 68 NS_ENSURE_SUCCESS(rv, rv); 69 if (!IsInUncomposedDoc()) { 70 return rv; 71 } 72 Document& doc = aContext.OwnerDoc(); 73 74 bool shouldProcessMeta = true; 75 // We don't want to call ProcessMETATag when we are pretty print 76 // the document 77 if (doc.IsXMLDocument()) { 78 if (nsCOMPtr<nsIXMLContentSink> xmlSink = 79 do_QueryInterface(doc.GetCurrentContentSink())) { 80 if (xmlSink->IsPrettyPrintXML() && 81 xmlSink->IsPrettyPrintHasSpecialRoot()) { 82 shouldProcessMeta = false; 83 } 84 } 85 } 86 87 if (shouldProcessMeta) { 88 doc.ProcessMETATag(this); 89 } 90 91 if (AttrValueIs(kNameSpaceID_None, nsGkAtoms::httpEquiv, nsGkAtoms::headerCSP, 92 eIgnoreCase)) { 93 // only accept <meta http-equiv="Content-Security-Policy" content=""> if it 94 // appears in the <head> element. 95 Element* headElt = doc.GetHeadElement(); 96 if (headElt && IsInclusiveDescendantOf(headElt)) { 97 nsAutoString content; 98 GetContent(content); 99 100 if (LOG_ENABLED()) { 101 nsAutoCString documentURIspec; 102 if (nsIURI* documentURI = doc.GetDocumentURI()) { 103 documentURI->GetAsciiSpec(documentURIspec); 104 } 105 106 LOG( 107 ("HTMLMetaElement %p sets CSP '%s' on document=%p, " 108 "document-uri=%s", 109 this, NS_ConvertUTF16toUTF8(content).get(), &doc, 110 documentURIspec.get())); 111 } 112 CSP_ApplyMetaCSPToDoc(doc, content); 113 } 114 } 115 116 if (const nsAttrValue* name = GetParsedAttr(nsGkAtoms::name)) { 117 MetaAddedOrChanged(doc, *name, ChangeKind::TreeChange); 118 } 119 CreateAndDispatchEvent(doc, u"DOMMetaAdded"_ns); 120 return rv; 121 } 122 123 void HTMLMetaElement::UnbindFromTree(UnbindContext& aContext) { 124 if (Document* oldDoc = GetUncomposedDoc()) { 125 if (const nsAttrValue* name = GetParsedAttr(nsGkAtoms::name)) { 126 MetaRemoved(*oldDoc, *name, ChangeKind::TreeChange); 127 } 128 CreateAndDispatchEvent(*oldDoc, u"DOMMetaRemoved"_ns); 129 } 130 nsGenericHTMLElement::UnbindFromTree(aContext); 131 } 132 133 void HTMLMetaElement::CreateAndDispatchEvent(Document&, 134 const nsAString& aEventName) { 135 AsyncEventDispatcher::RunDOMEventWhenSafe(*this, aEventName, CanBubble::eYes, 136 ChromeOnlyDispatch::eYes); 137 } 138 139 JSObject* HTMLMetaElement::WrapNode(JSContext* aCx, 140 JS::Handle<JSObject*> aGivenProto) { 141 return HTMLMetaElement_Binding::Wrap(aCx, this, aGivenProto); 142 } 143 144 void HTMLMetaElement::MetaAddedOrChanged(Document& aDoc, 145 const nsAttrValue& aName, 146 ChangeKind aChangeKind) { 147 nsAutoString content; 148 const bool hasContent = GetAttr(nsGkAtoms::content, content); 149 if (aName.Equals(nsGkAtoms::viewport, eIgnoreCase)) { 150 if (hasContent) { 151 aDoc.SetMetaViewportData(MakeUnique<ViewportMetaData>(content)); 152 } 153 return; 154 } 155 156 if (aName.Equals(nsGkAtoms::referrer, eIgnoreCase)) { 157 content = nsContentUtils::TrimWhitespace<nsContentUtils::IsHTMLWhitespace>( 158 content); 159 return aDoc.UpdateReferrerInfoFromMeta(content, 160 /* aPreload = */ false); 161 } 162 if (aName.Equals(nsGkAtoms::color_scheme, eIgnoreCase)) { 163 if (aChangeKind != ChangeKind::ContentChange) { 164 return aDoc.AddColorSchemeMeta(*this); 165 } 166 return aDoc.RecomputeColorScheme(); 167 } 168 } 169 170 void HTMLMetaElement::MetaRemoved(Document& aDoc, const nsAttrValue& aName, 171 ChangeKind aChangeKind) { 172 MOZ_ASSERT(aChangeKind != ChangeKind::ContentChange, 173 "Content change can't trigger removal"); 174 if (aName.Equals(nsGkAtoms::color_scheme, eIgnoreCase)) { 175 return aDoc.RemoveColorSchemeMeta(*this); 176 } 177 } 178 179 } // namespace mozilla::dom