/src/mozilla-central/dom/html/HTMLSharedElement.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | #include "mozilla/dom/HTMLBaseElementBinding.h" |
9 | | #include "mozilla/dom/HTMLDirectoryElementBinding.h" |
10 | | #include "mozilla/dom/HTMLHeadElementBinding.h" |
11 | | #include "mozilla/dom/HTMLHtmlElementBinding.h" |
12 | | #include "mozilla/dom/HTMLParamElementBinding.h" |
13 | | #include "mozilla/dom/HTMLQuoteElementBinding.h" |
14 | | |
15 | | #include "mozilla/AsyncEventDispatcher.h" |
16 | | #include "mozilla/MappedDeclarations.h" |
17 | | #include "nsAttrValueInlines.h" |
18 | | #include "nsStyleConsts.h" |
19 | | #include "nsMappedAttributes.h" |
20 | | #include "nsContentUtils.h" |
21 | | #include "nsIContentSecurityPolicy.h" |
22 | | #include "nsIURI.h" |
23 | | |
24 | | NS_IMPL_NS_NEW_HTML_ELEMENT(Shared) |
25 | | |
26 | | namespace mozilla { |
27 | | namespace dom { |
28 | | |
29 | | extern nsAttrValue::EnumTable kListTypeTable[]; |
30 | | |
31 | | HTMLSharedElement::~HTMLSharedElement() |
32 | 0 | { |
33 | 0 | } |
34 | | |
35 | | NS_IMPL_ELEMENT_CLONE(HTMLSharedElement) |
36 | | |
37 | | void |
38 | | HTMLSharedElement::GetHref(nsAString& aValue) |
39 | 0 | { |
40 | 0 | MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::base), |
41 | 0 | "This should only get called for <base> elements"); |
42 | 0 | nsAutoString href; |
43 | 0 | GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); |
44 | 0 |
|
45 | 0 | nsCOMPtr<nsIURI> uri; |
46 | 0 | nsIDocument* doc = OwnerDoc(); |
47 | 0 | nsContentUtils::NewURIWithDocumentCharset( |
48 | 0 | getter_AddRefs(uri), href, doc, doc->GetFallbackBaseURI()); |
49 | 0 |
|
50 | 0 | if (!uri) { |
51 | 0 | aValue = href; |
52 | 0 | return; |
53 | 0 | } |
54 | 0 | |
55 | 0 | nsAutoCString spec; |
56 | 0 | uri->GetSpec(spec); |
57 | 0 | CopyUTF8toUTF16(spec, aValue); |
58 | 0 | } |
59 | | |
60 | | void |
61 | | HTMLSharedElement::DoneAddingChildren(bool aHaveNotified) |
62 | 0 | { |
63 | 0 | if (mNodeInfo->Equals(nsGkAtoms::head)) { |
64 | 0 | nsCOMPtr<nsIDocument> doc = GetUncomposedDoc(); |
65 | 0 | if (doc) { |
66 | 0 | doc->OnL10nResourceContainerParsed(); |
67 | 0 | } |
68 | 0 |
|
69 | 0 | RefPtr<AsyncEventDispatcher> asyncDispatcher = |
70 | 0 | new AsyncEventDispatcher(this, |
71 | 0 | NS_LITERAL_STRING("DOMHeadElementParsed"), |
72 | 0 | CanBubble::eYes, |
73 | 0 | ChromeOnlyDispatch::eYes); |
74 | 0 | // Always run async in order to avoid running script when the content |
75 | 0 | // sink isn't expecting it. |
76 | 0 | asyncDispatcher->PostDOMEvent(); |
77 | 0 | } |
78 | 0 | } |
79 | | |
80 | | bool |
81 | | HTMLSharedElement::ParseAttribute(int32_t aNamespaceID, |
82 | | nsAtom* aAttribute, |
83 | | const nsAString& aValue, |
84 | | nsIPrincipal* aMaybeScriptedPrincipal, |
85 | | nsAttrValue& aResult) |
86 | 0 | { |
87 | 0 | if (aNamespaceID == kNameSpaceID_None && |
88 | 0 | mNodeInfo->Equals(nsGkAtoms::dir)) { |
89 | 0 | if (aAttribute == nsGkAtoms::type) { |
90 | 0 | return aResult.ParseEnumValue(aValue, mozilla::dom::kListTypeTable, false); |
91 | 0 | } |
92 | 0 | if (aAttribute == nsGkAtoms::start) { |
93 | 0 | return aResult.ParseIntWithBounds(aValue, 1); |
94 | 0 | } |
95 | 0 | } |
96 | 0 | |
97 | 0 | return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue, |
98 | 0 | aMaybeScriptedPrincipal, aResult); |
99 | 0 | } |
100 | | |
101 | | static void |
102 | | DirectoryMapAttributesIntoRule(const nsMappedAttributes* aAttributes, |
103 | | MappedDeclarations& aDecls) |
104 | 0 | { |
105 | 0 | if (!aDecls.PropertyIsSet(eCSSProperty_list_style_type)) { |
106 | 0 | // type: enum |
107 | 0 | const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::type); |
108 | 0 | if (value) { |
109 | 0 | if (value->Type() == nsAttrValue::eEnum) { |
110 | 0 | aDecls.SetKeywordValue(eCSSProperty_list_style_type, value->GetEnumValue()); |
111 | 0 | } else { |
112 | 0 | aDecls.SetKeywordValue(eCSSProperty_list_style_type, NS_STYLE_LIST_STYLE_DISC); |
113 | 0 | } |
114 | 0 | } |
115 | 0 | } |
116 | 0 |
|
117 | 0 | nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aDecls); |
118 | 0 | } |
119 | | |
120 | | NS_IMETHODIMP_(bool) |
121 | | HTMLSharedElement::IsAttributeMapped(const nsAtom* aAttribute) const |
122 | 0 | { |
123 | 0 | if (mNodeInfo->Equals(nsGkAtoms::dir)) { |
124 | 0 | static const MappedAttributeEntry attributes[] = { |
125 | 0 | { &nsGkAtoms::type }, |
126 | 0 | // { &nsGkAtoms::compact }, // XXX |
127 | 0 | { nullptr} |
128 | 0 | }; |
129 | 0 |
|
130 | 0 | static const MappedAttributeEntry* const map[] = { |
131 | 0 | attributes, |
132 | 0 | sCommonAttributeMap, |
133 | 0 | }; |
134 | 0 |
|
135 | 0 | return FindAttributeDependence(aAttribute, map); |
136 | 0 | } |
137 | 0 | |
138 | 0 | return nsGenericHTMLElement::IsAttributeMapped(aAttribute); |
139 | 0 | } |
140 | | |
141 | | static void |
142 | | SetBaseURIUsingFirstBaseWithHref(nsIDocument* aDocument, nsIContent* aMustMatch) |
143 | 0 | { |
144 | 0 | MOZ_ASSERT(aDocument, "Need a document!"); |
145 | 0 |
|
146 | 0 | for (nsIContent* child = aDocument->GetFirstChild(); child; |
147 | 0 | child = child->GetNextNode()) { |
148 | 0 | if (child->IsHTMLElement(nsGkAtoms::base) && |
149 | 0 | child->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { |
150 | 0 | if (aMustMatch && child != aMustMatch) { |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | |
154 | 0 | // Resolve the <base> element's href relative to our document's |
155 | 0 | // fallback base URI. |
156 | 0 | nsAutoString href; |
157 | 0 | child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href); |
158 | 0 |
|
159 | 0 | nsCOMPtr<nsIURI> newBaseURI; |
160 | 0 | nsContentUtils::NewURIWithDocumentCharset( |
161 | 0 | getter_AddRefs(newBaseURI), href, aDocument, |
162 | 0 | aDocument->GetFallbackBaseURI()); |
163 | 0 |
|
164 | 0 | // Check if CSP allows this base-uri |
165 | 0 | nsCOMPtr<nsIContentSecurityPolicy> csp; |
166 | 0 | nsresult rv = aDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp)); |
167 | 0 | NS_ASSERTION(NS_SUCCEEDED(rv), "Getting CSP Failed"); |
168 | 0 | // For all the different error cases we assign a nullptr to |
169 | 0 | // newBaseURI, so we basically call aDocument->SetBaseURI(nullptr); |
170 | 0 | if (NS_FAILED(rv)) { |
171 | 0 | newBaseURI = nullptr; |
172 | 0 | } |
173 | 0 | if (csp && newBaseURI) { |
174 | 0 | // base-uri is only enforced if explicitly defined in the |
175 | 0 | // policy - do *not* consult default-src, see: |
176 | 0 | // http://www.w3.org/TR/CSP2/#directive-default-src |
177 | 0 | bool cspPermitsBaseURI = true; |
178 | 0 | rv = csp->Permits(child->AsElement(), newBaseURI, |
179 | 0 | nsIContentSecurityPolicy::BASE_URI_DIRECTIVE, |
180 | 0 | true, &cspPermitsBaseURI); |
181 | 0 | if (NS_FAILED(rv) || !cspPermitsBaseURI) { |
182 | 0 | newBaseURI = nullptr; |
183 | 0 | } |
184 | 0 | } |
185 | 0 | aDocument->SetBaseURI(newBaseURI); |
186 | 0 | aDocument->SetChromeXHRDocBaseURI(nullptr); |
187 | 0 | return; |
188 | 0 | } |
189 | 0 | } |
190 | 0 |
|
191 | 0 | aDocument->SetBaseURI(nullptr); |
192 | 0 | } |
193 | | |
194 | | static void |
195 | | SetBaseTargetUsingFirstBaseWithTarget(nsIDocument* aDocument, |
196 | | nsIContent* aMustMatch) |
197 | 0 | { |
198 | 0 | MOZ_ASSERT(aDocument, "Need a document!"); |
199 | 0 |
|
200 | 0 | for (nsIContent* child = aDocument->GetFirstChild(); child; |
201 | 0 | child = child->GetNextNode()) { |
202 | 0 | if (child->IsHTMLElement(nsGkAtoms::base) && |
203 | 0 | child->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::target)) { |
204 | 0 | if (aMustMatch && child != aMustMatch) { |
205 | 0 | return; |
206 | 0 | } |
207 | 0 | |
208 | 0 | nsString target; |
209 | 0 | child->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::target, target); |
210 | 0 | aDocument->SetBaseTarget(target); |
211 | 0 | return; |
212 | 0 | } |
213 | 0 | } |
214 | 0 |
|
215 | 0 | aDocument->SetBaseTarget(EmptyString()); |
216 | 0 | } |
217 | | |
218 | | nsresult |
219 | | HTMLSharedElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName, |
220 | | const nsAttrValue* aValue, |
221 | | const nsAttrValue* aOldValue, |
222 | | nsIPrincipal* aSubjectPrincipal, |
223 | | bool aNotify) |
224 | 0 | { |
225 | 0 | if (aNamespaceID == kNameSpaceID_None) { |
226 | 0 | if (aName == nsGkAtoms::href) { |
227 | 0 | // If the href attribute of a <base> tag is changing, we may need to |
228 | 0 | // update the document's base URI, which will cause all the links on the |
229 | 0 | // page to be re-resolved given the new base. |
230 | 0 | // If the href is being unset (aValue is null), we will need to find a new |
231 | 0 | // <base>. |
232 | 0 | if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { |
233 | 0 | SetBaseURIUsingFirstBaseWithHref(GetUncomposedDoc(), |
234 | 0 | aValue ? this : nullptr); |
235 | 0 | } |
236 | 0 | } else if (aName == nsGkAtoms::target) { |
237 | 0 | // The target attribute is in pretty much the same situation as the href |
238 | 0 | // attribute, above. |
239 | 0 | if (mNodeInfo->Equals(nsGkAtoms::base) && IsInUncomposedDoc()) { |
240 | 0 | SetBaseTargetUsingFirstBaseWithTarget(GetUncomposedDoc(), |
241 | 0 | aValue ? this : nullptr); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | } |
245 | 0 |
|
246 | 0 | return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue, |
247 | 0 | aOldValue, aSubjectPrincipal, aNotify); |
248 | 0 | } |
249 | | |
250 | | nsresult |
251 | | HTMLSharedElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, |
252 | | nsIContent* aBindingParent) |
253 | 0 | { |
254 | 0 | nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent, |
255 | 0 | aBindingParent); |
256 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
257 | 0 |
|
258 | 0 | // The document stores a pointer to its base URI and base target, which we may |
259 | 0 | // need to update here. |
260 | 0 | if (mNodeInfo->Equals(nsGkAtoms::base) && |
261 | 0 | aDocument) { |
262 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { |
263 | 0 | SetBaseURIUsingFirstBaseWithHref(aDocument, this); |
264 | 0 | } |
265 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::target)) { |
266 | 0 | SetBaseTargetUsingFirstBaseWithTarget(aDocument, this); |
267 | 0 | } |
268 | 0 | } |
269 | 0 |
|
270 | 0 | return NS_OK; |
271 | 0 | } |
272 | | |
273 | | void |
274 | | HTMLSharedElement::UnbindFromTree(bool aDeep, bool aNullParent) |
275 | 0 | { |
276 | 0 | nsIDocument* doc = GetUncomposedDoc(); |
277 | 0 |
|
278 | 0 | nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent); |
279 | 0 |
|
280 | 0 | // If we're removing a <base> from a document, we may need to update the |
281 | 0 | // document's base URI and base target |
282 | 0 | if (doc && mNodeInfo->Equals(nsGkAtoms::base)) { |
283 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::href)) { |
284 | 0 | SetBaseURIUsingFirstBaseWithHref(doc, nullptr); |
285 | 0 | } |
286 | 0 | if (HasAttr(kNameSpaceID_None, nsGkAtoms::target)) { |
287 | 0 | SetBaseTargetUsingFirstBaseWithTarget(doc, nullptr); |
288 | 0 | } |
289 | 0 | } |
290 | 0 | } |
291 | | |
292 | | nsMapRuleToAttributesFunc |
293 | | HTMLSharedElement::GetAttributeMappingFunction() const |
294 | 0 | { |
295 | 0 | if (mNodeInfo->Equals(nsGkAtoms::dir)) { |
296 | 0 | return &DirectoryMapAttributesIntoRule; |
297 | 0 | } |
298 | 0 | |
299 | 0 | return nsGenericHTMLElement::GetAttributeMappingFunction(); |
300 | 0 | } |
301 | | |
302 | | JSObject* |
303 | | HTMLSharedElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) |
304 | 0 | { |
305 | 0 | if (mNodeInfo->Equals(nsGkAtoms::param)) { |
306 | 0 | return HTMLParamElement_Binding::Wrap(aCx, this, aGivenProto); |
307 | 0 | } |
308 | 0 | if (mNodeInfo->Equals(nsGkAtoms::base)) { |
309 | 0 | return HTMLBaseElement_Binding::Wrap(aCx, this, aGivenProto); |
310 | 0 | } |
311 | 0 | if (mNodeInfo->Equals(nsGkAtoms::dir)) { |
312 | 0 | return HTMLDirectoryElement_Binding::Wrap(aCx, this, aGivenProto); |
313 | 0 | } |
314 | 0 | if (mNodeInfo->Equals(nsGkAtoms::q) || |
315 | 0 | mNodeInfo->Equals(nsGkAtoms::blockquote)) { |
316 | 0 | return HTMLQuoteElement_Binding::Wrap(aCx, this, aGivenProto); |
317 | 0 | } |
318 | 0 | if (mNodeInfo->Equals(nsGkAtoms::head)) { |
319 | 0 | return HTMLHeadElement_Binding::Wrap(aCx, this, aGivenProto); |
320 | 0 | } |
321 | 0 | MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::html)); |
322 | 0 | return HTMLHtmlElement_Binding::Wrap(aCx, this, aGivenProto); |
323 | 0 | } |
324 | | |
325 | | } // namespace dom |
326 | | } // namespace mozilla |