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/HTMLContentElement.h"
8 : #include "mozilla/dom/HTMLContentElementBinding.h"
9 : #include "mozilla/dom/HTMLUnknownElement.h"
10 : #include "mozilla/dom/NodeListBinding.h"
11 : #include "mozilla/dom/ShadowRoot.h"
12 : #include "mozilla/css/StyleRule.h"
13 : #include "mozilla/GenericSpecifiedValuesInlines.h"
14 : #include "nsGkAtoms.h"
15 : #include "nsStyleConsts.h"
16 : #include "nsIAtom.h"
17 : #include "nsCSSRuleProcessor.h"
18 : #include "nsRuleProcessorData.h"
19 : #include "nsRuleWalker.h"
20 : #include "nsCSSParser.h"
21 : #include "nsDocument.h"
22 :
23 : // Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Content) to add check for web components
24 : // being enabled.
25 : nsGenericHTMLElement*
26 0 : NS_NewHTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
27 : mozilla::dom::FromParser aFromParser)
28 : {
29 : // When this check is removed, remove the nsDocument.h and
30 : // HTMLUnknownElement.h includes. Also remove nsINode::IsHTMLContentElement.
31 : //
32 : // We have to jump through some hoops to be able to produce both NodeInfo* and
33 : // already_AddRefed<NodeInfo>& for our callees.
34 0 : RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
35 0 : if (!nsDocument::IsWebComponentsEnabled(nodeInfo)) {
36 0 : already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
37 0 : return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
38 : }
39 :
40 0 : already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
41 0 : return new mozilla::dom::HTMLContentElement(nodeInfoArg);
42 : }
43 :
44 : using namespace mozilla::dom;
45 :
46 0 : HTMLContentElement::HTMLContentElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
47 0 : : nsGenericHTMLElement(aNodeInfo), mValidSelector(true), mIsInsertionPoint(false)
48 : {
49 0 : }
50 :
51 0 : HTMLContentElement::~HTMLContentElement()
52 : {
53 0 : }
54 :
55 0 : NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLContentElement,
56 : nsGenericHTMLElement,
57 : mMatchedNodes)
58 :
59 0 : NS_IMPL_ADDREF_INHERITED(HTMLContentElement, Element)
60 0 : NS_IMPL_RELEASE_INHERITED(HTMLContentElement, Element)
61 :
62 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLContentElement)
63 0 : NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
64 :
65 0 : NS_IMPL_ELEMENT_CLONE(HTMLContentElement)
66 :
67 : JSObject*
68 0 : HTMLContentElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
69 : {
70 0 : return HTMLContentElementBinding::Wrap(aCx, this, aGivenProto);
71 : }
72 :
73 : nsresult
74 0 : HTMLContentElement::BindToTree(nsIDocument* aDocument,
75 : nsIContent* aParent,
76 : nsIContent* aBindingParent,
77 : bool aCompileEventHandlers)
78 : {
79 0 : RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
80 :
81 0 : nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
82 : aBindingParent,
83 0 : aCompileEventHandlers);
84 0 : NS_ENSURE_SUCCESS(rv, rv);
85 :
86 0 : ShadowRoot* containingShadow = GetContainingShadow();
87 0 : if (containingShadow && !oldContainingShadow) {
88 0 : nsINode* parentNode = nsINode::GetParentNode();
89 0 : while (parentNode && parentNode != containingShadow) {
90 0 : if (parentNode->IsHTMLContentElement()) {
91 : // Content element in fallback content is not an insertion point.
92 0 : return NS_OK;
93 : }
94 0 : parentNode = parentNode->GetParentNode();
95 : }
96 :
97 : // If the content element is being inserted into a ShadowRoot,
98 : // add this element to the list of insertion points.
99 0 : mIsInsertionPoint = true;
100 0 : containingShadow->AddInsertionPoint(this);
101 0 : containingShadow->SetInsertionPointChanged();
102 : }
103 :
104 0 : return NS_OK;
105 : }
106 :
107 : void
108 0 : HTMLContentElement::UnbindFromTree(bool aDeep, bool aNullParent)
109 : {
110 0 : RefPtr<ShadowRoot> oldContainingShadow = GetContainingShadow();
111 :
112 0 : nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
113 :
114 0 : if (oldContainingShadow && !GetContainingShadow() && mIsInsertionPoint) {
115 0 : oldContainingShadow->RemoveInsertionPoint(this);
116 :
117 : // Remove all the matched nodes now that the
118 : // insertion point is no longer an insertion point.
119 0 : ClearMatchedNodes();
120 0 : oldContainingShadow->SetInsertionPointChanged();
121 :
122 0 : mIsInsertionPoint = false;
123 : }
124 0 : }
125 :
126 : void
127 0 : HTMLContentElement::AppendMatchedNode(nsIContent* aContent)
128 : {
129 0 : mMatchedNodes.AppendElement(aContent);
130 0 : nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints();
131 0 : destInsertionPoint.AppendElement(this);
132 :
133 0 : if (mMatchedNodes.Length() == 1) {
134 : // Fallback content gets dropped so we need to updated fallback
135 : // content distribution.
136 0 : UpdateFallbackDistribution();
137 : }
138 0 : }
139 :
140 : void
141 0 : HTMLContentElement::UpdateFallbackDistribution()
142 : {
143 0 : for (nsIContent* child = nsINode::GetFirstChild();
144 0 : child;
145 0 : child = child->GetNextSibling()) {
146 0 : nsTArray<nsIContent*>& destInsertionPoint = child->DestInsertionPoints();
147 0 : destInsertionPoint.Clear();
148 0 : if (mMatchedNodes.IsEmpty()) {
149 0 : destInsertionPoint.AppendElement(this);
150 : }
151 : }
152 0 : }
153 :
154 : void
155 0 : HTMLContentElement::RemoveMatchedNode(nsIContent* aContent)
156 : {
157 0 : mMatchedNodes.RemoveElement(aContent);
158 0 : ShadowRoot::RemoveDestInsertionPoint(this, aContent->DestInsertionPoints());
159 :
160 0 : if (mMatchedNodes.IsEmpty()) {
161 : // Fallback content is activated so we need to update fallback
162 : // content distribution.
163 0 : UpdateFallbackDistribution();
164 : }
165 0 : }
166 :
167 : void
168 0 : HTMLContentElement::InsertMatchedNode(uint32_t aIndex, nsIContent* aContent)
169 : {
170 0 : mMatchedNodes.InsertElementAt(aIndex, aContent);
171 0 : nsTArray<nsIContent*>& destInsertionPoint = aContent->DestInsertionPoints();
172 0 : destInsertionPoint.AppendElement(this);
173 :
174 0 : if (mMatchedNodes.Length() == 1) {
175 : // Fallback content gets dropped so we need to updated fallback
176 : // content distribution.
177 0 : UpdateFallbackDistribution();
178 : }
179 0 : }
180 :
181 : void
182 0 : HTMLContentElement::ClearMatchedNodes()
183 : {
184 0 : for (uint32_t i = 0; i < mMatchedNodes.Length(); i++) {
185 0 : ShadowRoot::RemoveDestInsertionPoint(this, mMatchedNodes[i]->DestInsertionPoints());
186 : }
187 :
188 0 : mMatchedNodes.Clear();
189 :
190 0 : UpdateFallbackDistribution();
191 0 : }
192 :
193 : static bool
194 0 : IsValidContentSelectors(nsCSSSelector* aSelector)
195 : {
196 0 : nsCSSSelector* currentSelector = aSelector;
197 0 : while (currentSelector) {
198 : // Blacklist invalid selector fragments.
199 0 : if (currentSelector->IsPseudoElement() ||
200 0 : currentSelector->mPseudoClassList ||
201 0 : currentSelector->mNegations ||
202 0 : currentSelector->mOperator) {
203 0 : return false;
204 : }
205 :
206 0 : currentSelector = currentSelector->mNext;
207 : }
208 :
209 0 : return true;
210 : }
211 :
212 : nsresult
213 0 : HTMLContentElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
214 : const nsAttrValue* aValue,
215 : const nsAttrValue* aOldValue, bool aNotify)
216 : {
217 0 : if (aNamespaceID == kNameSpaceID_None && aName == nsGkAtoms::select) {
218 0 : if (aValue) {
219 : // Select attribute was updated, the insertion point may match different
220 : // elements.
221 0 : nsIDocument* doc = OwnerDoc();
222 0 : nsCSSParser parser(doc->CSSLoader());
223 :
224 0 : mValidSelector = true;
225 0 : mSelectorList = nullptr;
226 :
227 0 : nsAutoString valueStr;
228 0 : aValue->ToString(valueStr);
229 0 : nsresult rv = parser.ParseSelectorString(valueStr,
230 : doc->GetDocumentURI(),
231 : // Bug 11240
232 : 0, // XXX get the line number!
233 0 : getter_Transfers(mSelectorList));
234 :
235 : // We don't want to return an exception if parsing failed because
236 : // the spec does not define it as an exception case.
237 0 : if (NS_SUCCEEDED(rv)) {
238 : // Ensure that all the selectors are valid
239 0 : nsCSSSelectorList* selectors = mSelectorList;
240 0 : while (selectors) {
241 0 : if (!IsValidContentSelectors(selectors->mSelectors)) {
242 : // If we have an invalid selector, we can not match anything.
243 0 : mValidSelector = false;
244 0 : mSelectorList = nullptr;
245 0 : break;
246 : }
247 0 : selectors = selectors->mNext;
248 : }
249 : }
250 :
251 0 : ShadowRoot* containingShadow = GetContainingShadow();
252 0 : if (containingShadow) {
253 0 : containingShadow->DistributeAllNodes();
254 : }
255 : } else {
256 : // The select attribute was removed. This insertion point becomes
257 : // a universal selector.
258 0 : mValidSelector = true;
259 0 : mSelectorList = nullptr;
260 :
261 0 : ShadowRoot* containingShadow = GetContainingShadow();
262 0 : if (containingShadow) {
263 0 : containingShadow->DistributeAllNodes();
264 : }
265 : }
266 : }
267 :
268 0 : return nsGenericHTMLElement::AfterSetAttr(aNamespaceID, aName, aValue,
269 0 : aOldValue, aNotify);
270 : }
271 :
272 : bool
273 0 : HTMLContentElement::Match(nsIContent* aContent)
274 : {
275 0 : if (!mValidSelector) {
276 0 : return false;
277 : }
278 :
279 0 : if (mSelectorList) {
280 0 : nsIDocument* doc = OwnerDoc();
281 0 : ShadowRoot* containingShadow = GetContainingShadow();
282 0 : nsIContent* host = containingShadow->GetHost();
283 :
284 : TreeMatchContext matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited,
285 0 : doc, TreeMatchContext::eNeverMatchVisited);
286 0 : matchingContext.SetHasSpecifiedScope();
287 0 : matchingContext.AddScopeElement(host->AsElement());
288 :
289 0 : if (!aContent->IsElement()) {
290 0 : return false;
291 : }
292 :
293 0 : return nsCSSRuleProcessor::SelectorListMatches(aContent->AsElement(),
294 : matchingContext,
295 0 : mSelectorList);
296 : }
297 :
298 0 : return true;
299 : }
300 :
301 : already_AddRefed<DistributedContentList>
302 0 : HTMLContentElement::GetDistributedNodes()
303 : {
304 0 : RefPtr<DistributedContentList> list = new DistributedContentList(this);
305 0 : return list.forget();
306 : }
307 :
308 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DistributedContentList, mParent,
309 : mDistributedNodes)
310 :
311 0 : NS_INTERFACE_TABLE_HEAD(DistributedContentList)
312 0 : NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
313 0 : NS_INTERFACE_TABLE(DistributedContentList, nsINodeList, nsIDOMNodeList)
314 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList)
315 0 : NS_INTERFACE_MAP_END
316 :
317 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList)
318 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList)
319 :
320 0 : DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement)
321 0 : : mParent(aHostElement)
322 : {
323 0 : if (aHostElement->IsInsertionPoint()) {
324 0 : if (aHostElement->MatchedNodes().IsEmpty()) {
325 : // Fallback content.
326 0 : nsINode* contentNode = aHostElement;
327 0 : for (nsIContent* content = contentNode->GetFirstChild();
328 0 : content;
329 0 : content = content->GetNextSibling()) {
330 0 : mDistributedNodes.AppendElement(content);
331 : }
332 : } else {
333 0 : mDistributedNodes.AppendElements(aHostElement->MatchedNodes());
334 : }
335 : }
336 0 : }
337 :
338 0 : DistributedContentList::~DistributedContentList()
339 : {
340 0 : }
341 :
342 : nsIContent*
343 0 : DistributedContentList::Item(uint32_t aIndex)
344 : {
345 0 : return mDistributedNodes.SafeElementAt(aIndex);
346 : }
347 :
348 : NS_IMETHODIMP
349 0 : DistributedContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
350 : {
351 0 : nsIContent* item = Item(aIndex);
352 0 : if (!item) {
353 0 : return NS_ERROR_FAILURE;
354 : }
355 :
356 0 : return CallQueryInterface(item, aReturn);
357 : }
358 :
359 : NS_IMETHODIMP
360 0 : DistributedContentList::GetLength(uint32_t* aLength)
361 : {
362 0 : *aLength = mDistributedNodes.Length();
363 0 : return NS_OK;
364 : }
365 :
366 : int32_t
367 0 : DistributedContentList::IndexOf(nsIContent* aContent)
368 : {
369 0 : return mDistributedNodes.IndexOf(aContent);
370 : }
371 :
372 : JSObject*
373 0 : DistributedContentList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
374 : {
375 0 : return NodeListBinding::Wrap(aCx, this, aGivenProto);
376 : }
377 :
|