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/. */
7 #include "mozilla/dom/HTMLSharedElement.h"
8 #include "mozilla/dom/BindContext.h"
9 #include "mozilla/dom/HTMLBaseElementBinding.h"
10 #include "mozilla/dom/HTMLDirectoryElementBinding.h"
11 #include "mozilla/dom/HTMLHeadElementBinding.h"
12 #include "mozilla/dom/HTMLHtmlElementBinding.h"
13 #include "mozilla/dom/HTMLParamElementBinding.h"
14 #include "mozilla/dom/HTMLQuoteElementBinding.h"
16 #include "mozilla/AsyncEventDispatcher.h"
17 #include "nsContentUtils.h"
18 #include "nsIContentSecurityPolicy.h"
21 NS_IMPL_NS_NEW_HTML_ELEMENT(Shared
)
23 namespace mozilla::dom
{
25 HTMLSharedElement::~HTMLSharedElement() = default;
27 NS_IMPL_ELEMENT_CLONE(HTMLSharedElement
)
29 void HTMLSharedElement::GetHref(nsAString
& aValue
) {
30 MOZ_ASSERT(mNodeInfo
->Equals(nsGkAtoms::base
),
31 "This should only get called for <base> elements");
33 GetAttr(nsGkAtoms::href
, href
);
36 Document
* doc
= OwnerDoc();
37 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), href
, doc
,
38 doc
->GetFallbackBaseURI());
47 CopyUTF8toUTF16(spec
, aValue
);
50 void HTMLSharedElement::DoneAddingChildren(bool aHaveNotified
) {
51 if (mNodeInfo
->Equals(nsGkAtoms::head
)) {
52 if (nsCOMPtr
<Document
> doc
= GetUncomposedDoc()) {
53 doc
->OnL10nResourceContainerParsed();
54 if (!doc
->IsLoadedAsData()) {
55 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
56 new AsyncEventDispatcher(this, u
"DOMHeadElementParsed"_ns
,
57 CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
58 // Always run async in order to avoid running script when the content
59 // sink isn't expecting it.
60 asyncDispatcher
->PostDOMEvent();
66 static void SetBaseURIUsingFirstBaseWithHref(Document
* aDocument
,
67 nsIContent
* aMustMatch
) {
68 MOZ_ASSERT(aDocument
, "Need a document!");
70 for (nsIContent
* child
= aDocument
->GetFirstChild(); child
;
71 child
= child
->GetNextNode()) {
72 if (child
->IsHTMLElement(nsGkAtoms::base
) &&
73 child
->AsElement()->HasAttr(nsGkAtoms::href
)) {
74 if (aMustMatch
&& child
!= aMustMatch
) {
78 // Resolve the <base> element's href relative to our document's
81 child
->AsElement()->GetAttr(nsGkAtoms::href
, href
);
83 nsCOMPtr
<nsIURI
> newBaseURI
;
84 nsContentUtils::NewURIWithDocumentCharset(
85 getter_AddRefs(newBaseURI
), href
, aDocument
,
86 aDocument
->GetFallbackBaseURI());
89 // <https://html.spec.whatwg.org/multipage/semantics.html#set-the-frozen-base-url>
91 if (newBaseURI
&& (newBaseURI
->SchemeIs("data") ||
92 newBaseURI
->SchemeIs("javascript"))) {
96 // Check if CSP allows this base-uri
97 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDocument
->GetCsp();
98 if (csp
&& newBaseURI
) {
99 // base-uri is only enforced if explicitly defined in the
100 // policy - do *not* consult default-src, see:
101 // http://www.w3.org/TR/CSP2/#directive-default-src
102 bool cspPermitsBaseURI
= true;
103 nsresult rv
= csp
->Permits(
104 child
->AsElement(), nullptr /* nsICSPEventListener */, newBaseURI
,
105 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE
, true /* aSpecific */,
106 true /* aSendViolationReports */, &cspPermitsBaseURI
);
107 if (NS_FAILED(rv
) || !cspPermitsBaseURI
) {
108 newBaseURI
= nullptr;
112 aDocument
->SetBaseURI(newBaseURI
);
113 aDocument
->SetChromeXHRDocBaseURI(nullptr);
118 aDocument
->SetBaseURI(nullptr);
121 static void SetBaseTargetUsingFirstBaseWithTarget(Document
* aDocument
,
122 nsIContent
* aMustMatch
) {
123 MOZ_ASSERT(aDocument
, "Need a document!");
125 for (nsIContent
* child
= aDocument
->GetFirstChild(); child
;
126 child
= child
->GetNextNode()) {
127 if (child
->IsHTMLElement(nsGkAtoms::base
) &&
128 child
->AsElement()->HasAttr(nsGkAtoms::target
)) {
129 if (aMustMatch
&& child
!= aMustMatch
) {
134 child
->AsElement()->GetAttr(nsGkAtoms::target
, target
);
135 aDocument
->SetBaseTarget(target
);
140 aDocument
->SetBaseTarget(u
""_ns
);
143 void HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
144 const nsAttrValue
* aValue
,
145 const nsAttrValue
* aOldValue
,
146 nsIPrincipal
* aSubjectPrincipal
,
148 if (aNamespaceID
== kNameSpaceID_None
) {
149 if (aName
== nsGkAtoms::href
) {
150 // If the href attribute of a <base> tag is changing, we may need to
151 // update the document's base URI, which will cause all the links on the
152 // page to be re-resolved given the new base.
153 // If the href is being unset (aValue is null), we will need to find a new
155 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
156 SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(),
157 aValue
? this : nullptr);
159 } else if (aName
== nsGkAtoms::target
) {
160 // The target attribute is in pretty much the same situation as the href
162 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
163 SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(),
164 aValue
? this : nullptr);
169 return nsGenericHTMLElement::AfterSetAttr(
170 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
173 nsresult
HTMLSharedElement::BindToTree(BindContext
& aContext
,
175 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
176 NS_ENSURE_SUCCESS(rv
, rv
);
178 // The document stores a pointer to its base URI and base target, which we may
179 // need to update here.
180 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
181 if (HasAttr(nsGkAtoms::href
)) {
182 SetBaseURIUsingFirstBaseWithHref(&aContext
.OwnerDoc(), this);
184 if (HasAttr(nsGkAtoms::target
)) {
185 SetBaseTargetUsingFirstBaseWithTarget(&aContext
.OwnerDoc(), this);
192 void HTMLSharedElement::UnbindFromTree(UnbindContext
& aContext
) {
193 Document
* doc
= GetUncomposedDoc();
195 nsGenericHTMLElement::UnbindFromTree(aContext
);
197 // If we're removing a <base> from a document, we may need to update the
198 // document's base URI and base target
199 if (doc
&& mNodeInfo
->Equals(nsGkAtoms::base
)) {
200 if (HasAttr(nsGkAtoms::href
)) {
201 SetBaseURIUsingFirstBaseWithHref(doc
, nullptr);
203 if (HasAttr(nsGkAtoms::target
)) {
204 SetBaseTargetUsingFirstBaseWithTarget(doc
, nullptr);
209 JSObject
* HTMLSharedElement::WrapNode(JSContext
* aCx
,
210 JS::Handle
<JSObject
*> aGivenProto
) {
211 if (mNodeInfo
->Equals(nsGkAtoms::param
)) {
212 return HTMLParamElement_Binding::Wrap(aCx
, this, aGivenProto
);
214 if (mNodeInfo
->Equals(nsGkAtoms::base
)) {
215 return HTMLBaseElement_Binding::Wrap(aCx
, this, aGivenProto
);
217 if (mNodeInfo
->Equals(nsGkAtoms::dir
)) {
218 return HTMLDirectoryElement_Binding::Wrap(aCx
, this, aGivenProto
);
220 if (mNodeInfo
->Equals(nsGkAtoms::q
) ||
221 mNodeInfo
->Equals(nsGkAtoms::blockquote
)) {
222 return HTMLQuoteElement_Binding::Wrap(aCx
, this, aGivenProto
);
224 if (mNodeInfo
->Equals(nsGkAtoms::head
)) {
225 return HTMLHeadElement_Binding::Wrap(aCx
, this, aGivenProto
);
227 MOZ_ASSERT(mNodeInfo
->Equals(nsGkAtoms::html
));
228 return HTMLHtmlElement_Binding::Wrap(aCx
, this, aGivenProto
);
231 } // namespace mozilla::dom