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());
88 // Check if CSP allows this base-uri
90 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDocument
->GetCsp();
91 if (csp
&& newBaseURI
) {
92 // base-uri is only enforced if explicitly defined in the
93 // policy - do *not* consult default-src, see:
94 // http://www.w3.org/TR/CSP2/#directive-default-src
95 bool cspPermitsBaseURI
= true;
97 child
->AsElement(), nullptr /* nsICSPEventListener */, newBaseURI
,
98 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE
, true /* aSpecific */,
99 true /* aSendViolationReports */, &cspPermitsBaseURI
);
100 if (NS_FAILED(rv
) || !cspPermitsBaseURI
) {
101 newBaseURI
= nullptr;
104 aDocument
->SetBaseURI(newBaseURI
);
105 aDocument
->SetChromeXHRDocBaseURI(nullptr);
110 aDocument
->SetBaseURI(nullptr);
113 static void SetBaseTargetUsingFirstBaseWithTarget(Document
* aDocument
,
114 nsIContent
* aMustMatch
) {
115 MOZ_ASSERT(aDocument
, "Need a document!");
117 for (nsIContent
* child
= aDocument
->GetFirstChild(); child
;
118 child
= child
->GetNextNode()) {
119 if (child
->IsHTMLElement(nsGkAtoms::base
) &&
120 child
->AsElement()->HasAttr(nsGkAtoms::target
)) {
121 if (aMustMatch
&& child
!= aMustMatch
) {
126 child
->AsElement()->GetAttr(nsGkAtoms::target
, target
);
127 aDocument
->SetBaseTarget(target
);
132 aDocument
->SetBaseTarget(u
""_ns
);
135 void HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
136 const nsAttrValue
* aValue
,
137 const nsAttrValue
* aOldValue
,
138 nsIPrincipal
* aSubjectPrincipal
,
140 if (aNamespaceID
== kNameSpaceID_None
) {
141 if (aName
== nsGkAtoms::href
) {
142 // If the href attribute of a <base> tag is changing, we may need to
143 // update the document's base URI, which will cause all the links on the
144 // page to be re-resolved given the new base.
145 // If the href is being unset (aValue is null), we will need to find a new
147 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
148 SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(),
149 aValue
? this : nullptr);
151 } else if (aName
== nsGkAtoms::target
) {
152 // The target attribute is in pretty much the same situation as the href
154 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
155 SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(),
156 aValue
? this : nullptr);
161 return nsGenericHTMLElement::AfterSetAttr(
162 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
165 nsresult
HTMLSharedElement::BindToTree(BindContext
& aContext
,
167 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
168 NS_ENSURE_SUCCESS(rv
, rv
);
170 // The document stores a pointer to its base URI and base target, which we may
171 // need to update here.
172 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
173 if (HasAttr(nsGkAtoms::href
)) {
174 SetBaseURIUsingFirstBaseWithHref(&aContext
.OwnerDoc(), this);
176 if (HasAttr(nsGkAtoms::target
)) {
177 SetBaseTargetUsingFirstBaseWithTarget(&aContext
.OwnerDoc(), this);
184 void HTMLSharedElement::UnbindFromTree(UnbindContext
& aContext
) {
185 Document
* doc
= GetUncomposedDoc();
187 nsGenericHTMLElement::UnbindFromTree(aContext
);
189 // If we're removing a <base> from a document, we may need to update the
190 // document's base URI and base target
191 if (doc
&& mNodeInfo
->Equals(nsGkAtoms::base
)) {
192 if (HasAttr(nsGkAtoms::href
)) {
193 SetBaseURIUsingFirstBaseWithHref(doc
, nullptr);
195 if (HasAttr(nsGkAtoms::target
)) {
196 SetBaseTargetUsingFirstBaseWithTarget(doc
, nullptr);
201 JSObject
* HTMLSharedElement::WrapNode(JSContext
* aCx
,
202 JS::Handle
<JSObject
*> aGivenProto
) {
203 if (mNodeInfo
->Equals(nsGkAtoms::param
)) {
204 return HTMLParamElement_Binding::Wrap(aCx
, this, aGivenProto
);
206 if (mNodeInfo
->Equals(nsGkAtoms::base
)) {
207 return HTMLBaseElement_Binding::Wrap(aCx
, this, aGivenProto
);
209 if (mNodeInfo
->Equals(nsGkAtoms::dir
)) {
210 return HTMLDirectoryElement_Binding::Wrap(aCx
, this, aGivenProto
);
212 if (mNodeInfo
->Equals(nsGkAtoms::q
) ||
213 mNodeInfo
->Equals(nsGkAtoms::blockquote
)) {
214 return HTMLQuoteElement_Binding::Wrap(aCx
, this, aGivenProto
);
216 if (mNodeInfo
->Equals(nsGkAtoms::head
)) {
217 return HTMLHeadElement_Binding::Wrap(aCx
, this, aGivenProto
);
219 MOZ_ASSERT(mNodeInfo
->Equals(nsGkAtoms::html
));
220 return HTMLHtmlElement_Binding::Wrap(aCx
, this, aGivenProto
);
223 } // namespace mozilla::dom