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 "mozilla/MappedDeclarations.h"
18 #include "nsAttrValueInlines.h"
19 #include "nsStyleConsts.h"
20 #include "nsMappedAttributes.h"
21 #include "nsContentUtils.h"
22 #include "nsIContentSecurityPolicy.h"
25 NS_IMPL_NS_NEW_HTML_ELEMENT(Shared
)
30 extern nsAttrValue::EnumTable kListTypeTable
[];
32 HTMLSharedElement::~HTMLSharedElement() {}
34 NS_IMPL_ELEMENT_CLONE(HTMLSharedElement
)
36 void HTMLSharedElement::GetHref(nsAString
& aValue
) {
37 MOZ_ASSERT(mNodeInfo
->Equals(nsGkAtoms::base
),
38 "This should only get called for <base> elements");
40 GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, href
);
43 Document
* doc
= OwnerDoc();
44 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), href
, doc
,
45 doc
->GetFallbackBaseURI());
54 CopyUTF8toUTF16(spec
, aValue
);
57 void HTMLSharedElement::DoneAddingChildren(bool aHaveNotified
) {
58 if (mNodeInfo
->Equals(nsGkAtoms::head
)) {
59 nsCOMPtr
<Document
> doc
= GetUncomposedDoc();
61 doc
->OnL10nResourceContainerParsed();
64 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
= new AsyncEventDispatcher(
65 this, NS_LITERAL_STRING("DOMHeadElementParsed"), CanBubble::eYes
,
66 ChromeOnlyDispatch::eYes
);
67 // Always run async in order to avoid running script when the content
68 // sink isn't expecting it.
69 asyncDispatcher
->PostDOMEvent();
73 bool HTMLSharedElement::ParseAttribute(int32_t aNamespaceID
, nsAtom
* aAttribute
,
74 const nsAString
& aValue
,
75 nsIPrincipal
* aMaybeScriptedPrincipal
,
76 nsAttrValue
& aResult
) {
77 if (aNamespaceID
== kNameSpaceID_None
&& mNodeInfo
->Equals(nsGkAtoms::dir
)) {
78 if (aAttribute
== nsGkAtoms::type
) {
79 return aResult
.ParseEnumValue(aValue
, mozilla::dom::kListTypeTable
,
82 if (aAttribute
== nsGkAtoms::start
) {
83 return aResult
.ParseIntWithBounds(aValue
, 1);
87 return nsGenericHTMLElement::ParseAttribute(aNamespaceID
, aAttribute
, aValue
,
88 aMaybeScriptedPrincipal
, aResult
);
91 static void DirectoryMapAttributesIntoRule(
92 const nsMappedAttributes
* aAttributes
, MappedDeclarations
& aDecls
) {
93 if (!aDecls
.PropertyIsSet(eCSSProperty_list_style_type
)) {
95 const nsAttrValue
* value
= aAttributes
->GetAttr(nsGkAtoms::type
);
97 if (value
->Type() == nsAttrValue::eEnum
) {
98 aDecls
.SetKeywordValue(eCSSProperty_list_style_type
,
99 value
->GetEnumValue());
101 aDecls
.SetKeywordValue(eCSSProperty_list_style_type
,
102 NS_STYLE_LIST_STYLE_DISC
);
107 nsGenericHTMLElement::MapCommonAttributesInto(aAttributes
, aDecls
);
111 HTMLSharedElement::IsAttributeMapped(const nsAtom
* aAttribute
) const {
112 if (mNodeInfo
->Equals(nsGkAtoms::dir
)) {
113 static const MappedAttributeEntry attributes
[] = {
115 // { nsGkAtoms::compact }, // XXX
118 static const MappedAttributeEntry
* const map
[] = {
123 return FindAttributeDependence(aAttribute
, map
);
126 return nsGenericHTMLElement::IsAttributeMapped(aAttribute
);
129 static void SetBaseURIUsingFirstBaseWithHref(Document
* aDocument
,
130 nsIContent
* aMustMatch
) {
131 MOZ_ASSERT(aDocument
, "Need a document!");
133 for (nsIContent
* child
= aDocument
->GetFirstChild(); child
;
134 child
= child
->GetNextNode()) {
135 if (child
->IsHTMLElement(nsGkAtoms::base
) &&
136 child
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
137 if (aMustMatch
&& child
!= aMustMatch
) {
141 // Resolve the <base> element's href relative to our document's
142 // fallback base URI.
144 child
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, href
);
146 nsCOMPtr
<nsIURI
> newBaseURI
;
147 nsContentUtils::NewURIWithDocumentCharset(
148 getter_AddRefs(newBaseURI
), href
, aDocument
,
149 aDocument
->GetFallbackBaseURI());
151 // Check if CSP allows this base-uri
153 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aDocument
->GetCsp();
154 if (csp
&& newBaseURI
) {
155 // base-uri is only enforced if explicitly defined in the
156 // policy - do *not* consult default-src, see:
157 // http://www.w3.org/TR/CSP2/#directive-default-src
158 bool cspPermitsBaseURI
= true;
159 rv
= csp
->Permits(child
->AsElement(), nullptr /* nsICSPEventListener */,
161 nsIContentSecurityPolicy::BASE_URI_DIRECTIVE
, true,
163 if (NS_FAILED(rv
) || !cspPermitsBaseURI
) {
164 newBaseURI
= nullptr;
167 aDocument
->SetBaseURI(newBaseURI
);
168 aDocument
->SetChromeXHRDocBaseURI(nullptr);
173 aDocument
->SetBaseURI(nullptr);
176 static void SetBaseTargetUsingFirstBaseWithTarget(Document
* aDocument
,
177 nsIContent
* aMustMatch
) {
178 MOZ_ASSERT(aDocument
, "Need a document!");
180 for (nsIContent
* child
= aDocument
->GetFirstChild(); child
;
181 child
= child
->GetNextNode()) {
182 if (child
->IsHTMLElement(nsGkAtoms::base
) &&
183 child
->AsElement()->HasAttr(kNameSpaceID_None
, nsGkAtoms::target
)) {
184 if (aMustMatch
&& child
!= aMustMatch
) {
189 child
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, target
);
190 aDocument
->SetBaseTarget(target
);
195 aDocument
->SetBaseTarget(EmptyString());
198 nsresult
HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID
, nsAtom
* aName
,
199 const nsAttrValue
* aValue
,
200 const nsAttrValue
* aOldValue
,
201 nsIPrincipal
* aSubjectPrincipal
,
203 if (aNamespaceID
== kNameSpaceID_None
) {
204 if (aName
== nsGkAtoms::href
) {
205 // If the href attribute of a <base> tag is changing, we may need to
206 // update the document's base URI, which will cause all the links on the
207 // page to be re-resolved given the new base.
208 // If the href is being unset (aValue is null), we will need to find a new
210 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
211 SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(),
212 aValue
? this : nullptr);
214 } else if (aName
== nsGkAtoms::target
) {
215 // The target attribute is in pretty much the same situation as the href
217 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
218 SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(),
219 aValue
? this : nullptr);
224 return nsGenericHTMLElement::AfterSetAttr(
225 aNamespaceID
, aName
, aValue
, aOldValue
, aSubjectPrincipal
, aNotify
);
228 nsresult
HTMLSharedElement::BindToTree(BindContext
& aContext
,
230 nsresult rv
= nsGenericHTMLElement::BindToTree(aContext
, aParent
);
231 NS_ENSURE_SUCCESS(rv
, rv
);
233 // The document stores a pointer to its base URI and base target, which we may
234 // need to update here.
235 if (mNodeInfo
->Equals(nsGkAtoms::base
) && IsInUncomposedDoc()) {
236 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
237 SetBaseURIUsingFirstBaseWithHref(&aContext
.OwnerDoc(), this);
239 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::target
)) {
240 SetBaseTargetUsingFirstBaseWithTarget(&aContext
.OwnerDoc(), this);
247 void HTMLSharedElement::UnbindFromTree(bool aNullParent
) {
248 Document
* doc
= GetUncomposedDoc();
250 nsGenericHTMLElement::UnbindFromTree(aNullParent
);
252 // If we're removing a <base> from a document, we may need to update the
253 // document's base URI and base target
254 if (doc
&& mNodeInfo
->Equals(nsGkAtoms::base
)) {
255 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::href
)) {
256 SetBaseURIUsingFirstBaseWithHref(doc
, nullptr);
258 if (HasAttr(kNameSpaceID_None
, nsGkAtoms::target
)) {
259 SetBaseTargetUsingFirstBaseWithTarget(doc
, nullptr);
264 nsMapRuleToAttributesFunc
HTMLSharedElement::GetAttributeMappingFunction()
266 if (mNodeInfo
->Equals(nsGkAtoms::dir
)) {
267 return &DirectoryMapAttributesIntoRule
;
270 return nsGenericHTMLElement::GetAttributeMappingFunction();
273 JSObject
* HTMLSharedElement::WrapNode(JSContext
* aCx
,
274 JS::Handle
<JSObject
*> aGivenProto
) {
275 if (mNodeInfo
->Equals(nsGkAtoms::param
)) {
276 return HTMLParamElement_Binding::Wrap(aCx
, this, aGivenProto
);
278 if (mNodeInfo
->Equals(nsGkAtoms::base
)) {
279 return HTMLBaseElement_Binding::Wrap(aCx
, this, aGivenProto
);
281 if (mNodeInfo
->Equals(nsGkAtoms::dir
)) {
282 return HTMLDirectoryElement_Binding::Wrap(aCx
, this, aGivenProto
);
284 if (mNodeInfo
->Equals(nsGkAtoms::q
) ||
285 mNodeInfo
->Equals(nsGkAtoms::blockquote
)) {
286 return HTMLQuoteElement_Binding::Wrap(aCx
, this, aGivenProto
);
288 if (mNodeInfo
->Equals(nsGkAtoms::head
)) {
289 return HTMLHeadElement_Binding::Wrap(aCx
, this, aGivenProto
);
291 MOZ_ASSERT(mNodeInfo
->Equals(nsGkAtoms::html
));
292 return HTMLHtmlElement_Binding::Wrap(aCx
, this, aGivenProto
);
296 } // namespace mozilla