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