HTMLSharedElement.cpp (8397B)
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/HTMLSharedElement.h" 8 9 #include "mozilla/AsyncEventDispatcher.h" 10 #include "mozilla/dom/BindContext.h" 11 #include "mozilla/dom/HTMLBaseElementBinding.h" 12 #include "mozilla/dom/HTMLDirectoryElementBinding.h" 13 #include "mozilla/dom/HTMLHeadElementBinding.h" 14 #include "mozilla/dom/HTMLHtmlElementBinding.h" 15 #include "mozilla/dom/HTMLParamElementBinding.h" 16 #include "mozilla/dom/HTMLQuoteElementBinding.h" 17 #include "mozilla/dom/PolicyContainer.h" 18 #include "nsContentUtils.h" 19 #include "nsIContentSecurityPolicy.h" 20 #include "nsIURI.h" 21 22 NS_IMPL_NS_NEW_HTML_ELEMENT(Shared) 23 24 namespace mozilla::dom { 25 26 HTMLSharedElement::~HTMLSharedElement() = default; 27 28 NS_IMPL_ELEMENT_CLONE(HTMLSharedElement) 29 30 void HTMLSharedElement::GetHref(nsAString& aValue) { 31 MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::base), 32 "This should only get called for <base> elements"); 33 nsAutoString href; 34 GetAttr(nsGkAtoms::href, href); 35 36 nsCOMPtr<nsIURI> uri; 37 Document* doc = OwnerDoc(); 38 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), href, doc, 39 doc->GetFallbackBaseURI()); 40 41 if (!uri) { 42 aValue = href; 43 return; 44 } 45 46 nsAutoCString spec; 47 uri->GetSpec(spec); 48 CopyUTF8toUTF16(spec, aValue); 49 } 50 51 void HTMLSharedElement::DoneAddingChildren(bool aHaveNotified) { 52 if (mNodeInfo->Equals(nsGkAtoms::head)) { 53 if (nsCOMPtr<Document> doc = GetUncomposedDoc()) { 54 doc->OnL10nResourceContainerParsed(); 55 if (!doc->IsLoadedAsData()) { 56 RefPtr<AsyncEventDispatcher> asyncDispatcher = 57 new AsyncEventDispatcher(this, u"DOMHeadElementParsed"_ns, 58 CanBubble::eYes, ChromeOnlyDispatch::eYes); 59 // Always run async in order to avoid running script when the content 60 // sink isn't expecting it. 61 asyncDispatcher->PostDOMEvent(); 62 } 63 } 64 } 65 } 66 67 static void SetBaseURIUsingFirstBaseWithHref(Document* aDocument, 68 nsIContent* aMustMatch) { 69 MOZ_ASSERT(aDocument, "Need a document!"); 70 71 for (nsIContent* child = aDocument->GetFirstChild(); child; 72 child = child->GetNextNode()) { 73 if (child->IsHTMLElement(nsGkAtoms::base) && 74 child->AsElement()->HasAttr(nsGkAtoms::href)) { 75 if (aMustMatch && child != aMustMatch) { 76 return; 77 } 78 79 // Resolve the <base> element's href relative to our document's 80 // fallback base URI. 81 nsAutoString href; 82 child->AsElement()->GetAttr(nsGkAtoms::href, href); 83 84 nsCOMPtr<nsIURI> newBaseURI; 85 nsContentUtils::NewURIWithDocumentCharset( 86 getter_AddRefs(newBaseURI), href, aDocument, 87 aDocument->GetFallbackBaseURI()); 88 89 // Vaguely based on 90 // <https://html.spec.whatwg.org/multipage/semantics.html#set-the-frozen-base-url> 91 92 if (newBaseURI && (newBaseURI->SchemeIs("data") || 93 newBaseURI->SchemeIs("javascript"))) { 94 newBaseURI = nullptr; 95 } 96 97 // Check if CSP allows this base-uri 98 nsCOMPtr<nsIContentSecurityPolicy> csp = 99 PolicyContainer::GetCSP(aDocument->GetPolicyContainer()); 100 if (csp && newBaseURI) { 101 // base-uri is only enforced if explicitly defined in the 102 // policy - do *not* consult default-src, see: 103 // http://www.w3.org/TR/CSP2/#directive-default-src 104 bool cspPermitsBaseURI = true; 105 nsresult rv = csp->Permits( 106 child->AsElement(), nullptr /* nsICSPEventListener */, newBaseURI, 107 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE, true /* aSpecific */, 108 true /* aSendViolationReports */, &cspPermitsBaseURI); 109 if (NS_FAILED(rv) || !cspPermitsBaseURI) { 110 newBaseURI = nullptr; 111 } 112 } 113 114 aDocument->SetBaseURI(newBaseURI); 115 aDocument->SetChromeXHRDocBaseURI(nullptr); 116 return; 117 } 118 } 119 120 aDocument->SetBaseURI(nullptr); 121 } 122 123 static void SetBaseTargetUsingFirstBaseWithTarget(Document* aDocument, 124 nsIContent* aMustMatch) { 125 MOZ_ASSERT(aDocument, "Need a document!"); 126 127 for (nsIContent* child = aDocument->GetFirstChild(); child; 128 child = child->GetNextNode()) { 129 if (child->IsHTMLElement(nsGkAtoms::base) && 130 child->AsElement()->HasAttr(nsGkAtoms::target)) { 131 if (aMustMatch && child != aMustMatch) { 132 return; 133 } 134 135 nsString target; 136 child->AsElement()->GetAttr(nsGkAtoms::target, target); 137 aDocument->SetBaseTarget(target); 138 return; 139 } 140 } 141 142 aDocument->SetBaseTarget(u""_ns); 143 } 144 145 void HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, 146 const nsAttrValue* aValue, 147 const nsAttrValue* aOldValue, 148 nsIPrincipal* aSubjectPrincipal, 149 bool aNotify) { 150 if (aNamespaceID == kNameSpaceID_None) { 151 if (aName == nsGkAtoms::href) { 152 // If the href attribute of a <base> tag is changing, we may need to 153 // update the document's base URI, which will cause all the links on the 154 // page to be re-resolved given the new base. 155 // If the href is being unset (aValue is null), we will need to find a new 156 // <base>. 157 if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { 158 SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), 159 aValue ? this : nullptr); 160 } 161 } else if (aName == nsGkAtoms::target) { 162 // The target attribute is in pretty much the same situation as the href 163 // attribute, above. 164 if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { 165 SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), 166 aValue ? this : nullptr); 167 } 168 } 169 } 170 171 return nsGenericHTMLElement::AfterSetAttr( 172 aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify); 173 } 174 175 nsresult HTMLSharedElement::BindToTree(BindContext& aContext, 176 nsINode& aParent) { 177 nsresult rv = nsGenericHTMLElement::BindToTree(aContext, aParent); 178 NS_ENSURE_SUCCESS(rv, rv); 179 180 // The document stores a pointer to its base URI and base target, which we may 181 // need to update here. 182 if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { 183 if (HasAttr(nsGkAtoms::href)) { 184 SetBaseURIUsingFirstBaseWithHref(&aContext.OwnerDoc(), this); 185 } 186 if (HasAttr(nsGkAtoms::target)) { 187 SetBaseTargetUsingFirstBaseWithTarget(&aContext.OwnerDoc(), this); 188 } 189 } 190 191 return NS_OK; 192 } 193 194 void HTMLSharedElement::UnbindFromTree(UnbindContext& aContext) { 195 Document* doc = GetUncomposedDoc(); 196 197 nsGenericHTMLElement::UnbindFromTree(aContext); 198 199 // If we're removing a <base> from a document, we may need to update the 200 // document's base URI and base target 201 if (doc && mNodeInfo->Equals(nsGkAtoms::base)) { 202 if (HasAttr(nsGkAtoms::href)) { 203 SetBaseURIUsingFirstBaseWithHref(doc, nullptr); 204 } 205 if (HasAttr(nsGkAtoms::target)) { 206 SetBaseTargetUsingFirstBaseWithTarget(doc, nullptr); 207 } 208 } 209 } 210 211 JSObject* HTMLSharedElement::WrapNode(JSContext* aCx, 212 JS::Handle<JSObject*> aGivenProto) { 213 if (mNodeInfo->Equals(nsGkAtoms::param)) { 214 return HTMLParamElement_Binding::Wrap(aCx, this, aGivenProto); 215 } 216 if (mNodeInfo->Equals(nsGkAtoms::base)) { 217 return HTMLBaseElement_Binding::Wrap(aCx, this, aGivenProto); 218 } 219 if (mNodeInfo->Equals(nsGkAtoms::dir)) { 220 return HTMLDirectoryElement_Binding::Wrap(aCx, this, aGivenProto); 221 } 222 if (mNodeInfo->Equals(nsGkAtoms::q) || 223 mNodeInfo->Equals(nsGkAtoms::blockquote)) { 224 return HTMLQuoteElement_Binding::Wrap(aCx, this, aGivenProto); 225 } 226 if (mNodeInfo->Equals(nsGkAtoms::head)) { 227 return HTMLHeadElement_Binding::Wrap(aCx, this, aGivenProto); 228 } 229 MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::html)); 230 return HTMLHtmlElement_Binding::Wrap(aCx, this, aGivenProto); 231 } 232 233 } // namespace mozilla::dom