1 /*
2  * Copyright (C) 2012 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Neither the name of Google Inc. nor the names of its
11  * contributors may be used to endorse or promote products derived from
12  * this software without specific prior written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "third_party/blink/renderer/core/dom/pseudo_element.h"
28 
29 #include <utility>
30 
31 #include "third_party/blink/renderer/core/dom/element_rare_data.h"
32 #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h"
33 #include "third_party/blink/renderer/core/dom/node_computed_style.h"
34 #include "third_party/blink/renderer/core/frame/web_feature.h"
35 #include "third_party/blink/renderer/core/layout/generated_children.h"
36 #include "third_party/blink/renderer/core/layout/layout_object.h"
37 #include "third_party/blink/renderer/core/layout/layout_quote.h"
38 #include "third_party/blink/renderer/core/layout/list_marker.h"
39 #include "third_party/blink/renderer/core/probe/core_probes.h"
40 #include "third_party/blink/renderer/core/style/computed_style.h"
41 #include "third_party/blink/renderer/core/style/content_data.h"
42 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
43 
44 namespace blink {
45 
Create(Element * parent,PseudoId pseudo_id)46 PseudoElement* PseudoElement::Create(Element* parent, PseudoId pseudo_id) {
47   if (pseudo_id == kPseudoIdFirstLetter)
48     return MakeGarbageCollected<FirstLetterPseudoElement>(parent);
49   DCHECK(pseudo_id == kPseudoIdAfter || pseudo_id == kPseudoIdBefore ||
50          pseudo_id == kPseudoIdBackdrop || pseudo_id == kPseudoIdMarker);
51   return MakeGarbageCollected<PseudoElement>(parent, pseudo_id);
52 }
53 
PseudoElementTagName(PseudoId pseudo_id)54 const QualifiedName& PseudoElementTagName(PseudoId pseudo_id) {
55   switch (pseudo_id) {
56     case kPseudoIdAfter: {
57       DEFINE_STATIC_LOCAL(QualifiedName, after,
58                           (g_null_atom, "::after", g_null_atom));
59       return after;
60     }
61     case kPseudoIdBefore: {
62       DEFINE_STATIC_LOCAL(QualifiedName, before,
63                           (g_null_atom, "::before", g_null_atom));
64       return before;
65     }
66     case kPseudoIdBackdrop: {
67       DEFINE_STATIC_LOCAL(QualifiedName, backdrop,
68                           (g_null_atom, "::backdrop", g_null_atom));
69       return backdrop;
70     }
71     case kPseudoIdFirstLetter: {
72       DEFINE_STATIC_LOCAL(QualifiedName, first_letter,
73                           (g_null_atom, "::first-letter", g_null_atom));
74       return first_letter;
75     }
76     case kPseudoIdMarker: {
77       DEFINE_STATIC_LOCAL(QualifiedName, marker,
78                           (g_null_atom, "::marker", g_null_atom));
79       return marker;
80     }
81     default:
82       NOTREACHED();
83   }
84   DEFINE_STATIC_LOCAL(QualifiedName, name,
85                       (g_null_atom, "::unknown", g_null_atom));
86   return name;
87 }
88 
PseudoElementNameForEvents(PseudoId pseudo_id)89 const AtomicString& PseudoElement::PseudoElementNameForEvents(
90     PseudoId pseudo_id) {
91   if (pseudo_id == kPseudoIdNone)
92     return g_null_atom;
93   else
94     return PseudoElementTagName(pseudo_id).LocalName();
95 }
96 
IsWebExposed(PseudoId pseudo_id,const Node * parent)97 bool PseudoElement::IsWebExposed(PseudoId pseudo_id, const Node* parent) {
98   switch (pseudo_id) {
99     case kPseudoIdMarker:
100       if (parent && parent->IsPseudoElement())
101         return RuntimeEnabledFeatures::CSSMarkerNestedPseudoElementEnabled();
102       return RuntimeEnabledFeatures::CSSMarkerPseudoElementEnabled();
103     default:
104       return true;
105   }
106 }
107 
PseudoElement(Element * parent,PseudoId pseudo_id)108 PseudoElement::PseudoElement(Element* parent, PseudoId pseudo_id)
109     : Element(PseudoElementTagName(pseudo_id),
110               &parent->GetDocument(),
111               kCreateElement),
112       pseudo_id_(pseudo_id) {
113   DCHECK_NE(pseudo_id, kPseudoIdNone);
114   parent->GetTreeScope().AdoptIfNeeded(*this);
115   SetParentOrShadowHostNode(parent);
116   SetHasCustomStyleCallbacks();
117   if ((pseudo_id == kPseudoIdBefore || pseudo_id == kPseudoIdAfter) &&
118       parent->HasTagName(html_names::kInputTag)) {
119     UseCounter::Count(parent->GetDocument(),
120                       WebFeature::kPseudoBeforeAfterForInputElement);
121   }
122 }
123 
CustomStyleForLayoutObject()124 scoped_refptr<ComputedStyle> PseudoElement::CustomStyleForLayoutObject() {
125   return ParentOrShadowHostElement()->StyleForPseudoElement(
126       PseudoElementStyleRequest(pseudo_id_));
127 }
128 
LayoutStyleForDisplayContents(const ComputedStyle & style)129 scoped_refptr<ComputedStyle> PseudoElement::LayoutStyleForDisplayContents(
130     const ComputedStyle& style) {
131   // For display:contents we should not generate a box, but we generate a non-
132   // observable inline box for pseudo elements to be able to locate the
133   // anonymous layout objects for generated content during DetachLayoutTree().
134   scoped_refptr<ComputedStyle> layout_style = ComputedStyle::Create();
135   layout_style->InheritFrom(style);
136   layout_style->SetContent(style.GetContentData());
137   layout_style->SetDisplay(EDisplay::kInline);
138   layout_style->SetStyleType(pseudo_id_);
139   return layout_style;
140 }
141 
Dispose()142 void PseudoElement::Dispose() {
143   DCHECK(ParentOrShadowHostElement());
144 
145   probe::PseudoElementDestroyed(this);
146 
147   DCHECK(!nextSibling());
148   DCHECK(!previousSibling());
149 
150   DetachLayoutTree();
151   Element* parent = ParentOrShadowHostElement();
152   GetDocument().AdoptIfNeeded(*this);
153   SetParentOrShadowHostNode(nullptr);
154   RemovedFrom(*parent);
155 }
156 
AttachLayoutTreeScope(PseudoElement * element)157 PseudoElement::AttachLayoutTreeScope::AttachLayoutTreeScope(
158     PseudoElement* element)
159     : element_(element) {
160   if (const ComputedStyle* style = element->GetComputedStyle()) {
161     if (style->Display() == EDisplay::kContents) {
162       original_style_ = style;
163       element->SetComputedStyle(element->LayoutStyleForDisplayContents(*style));
164     }
165   }
166 }
167 
~AttachLayoutTreeScope()168 PseudoElement::AttachLayoutTreeScope::~AttachLayoutTreeScope() {
169   if (original_style_)
170     element_->SetComputedStyle(std::move(original_style_));
171 }
172 
AttachLayoutTree(AttachContext & context)173 void PseudoElement::AttachLayoutTree(AttachContext& context) {
174   DCHECK(!GetLayoutObject());
175 
176   // Some elements may have 'display: list-item' but not be list items.
177   // Do not create a layout object for the ::marker in that case.
178   if (pseudo_id_ == kPseudoIdMarker) {
179     LayoutObject* originating_layout = parentNode()->GetLayoutObject();
180     if (!originating_layout || !originating_layout->IsListItemIncludingNG()) {
181       Node::AttachLayoutTree(context);
182       return;
183     }
184   }
185 
186   {
187     AttachLayoutTreeScope scope(this);
188     Element::AttachLayoutTree(context);
189   }
190   LayoutObject* layout_object = GetLayoutObject();
191   if (!layout_object)
192     return;
193 
194   // This is to ensure that bypassing the CanHaveGeneratedChildren() check in
195   // LayoutTreeBuilderForElement::ShouldCreateLayoutObject() does not result in
196   // the backdrop pseudo element's layout object becoming the child of a layout
197   // object that doesn't allow children.
198   DCHECK(layout_object->Parent());
199   DCHECK(CanHaveGeneratedChildren(*layout_object->Parent()));
200 
201   const ComputedStyle& style = layout_object->StyleRef();
202   switch (pseudo_id_) {
203     case kPseudoIdMarker: {
204       if (ListMarker* marker = ListMarker::Get(layout_object))
205         marker->UpdateMarkerContentIfNeeded(*layout_object);
206       if (style.ContentBehavesAsNormal())
207         return;
208       break;
209     }
210     case kPseudoIdBefore:
211     case kPseudoIdAfter:
212       break;
213     default:
214       return;
215   }
216 
217   DCHECK(!style.ContentBehavesAsNormal());
218   DCHECK(!style.ContentPreventsBoxGeneration());
219   for (const ContentData* content = style.GetContentData(); content;
220        content = content->Next()) {
221     LegacyLayout legacy = context.force_legacy_layout ? LegacyLayout::kForce
222                                                       : LegacyLayout::kAuto;
223     if (!content->IsAltText()) {
224       LayoutObject* child = content->CreateLayoutObject(*this, style, legacy);
225       if (layout_object->IsChildAllowed(child, style)) {
226         layout_object->AddChild(child);
227         if (child->IsQuote())
228           To<LayoutQuote>(child)->AttachQuote();
229       } else {
230         child->Destroy();
231       }
232     }
233   }
234 }
235 
LayoutObjectIsNeeded(const ComputedStyle & style) const236 bool PseudoElement::LayoutObjectIsNeeded(const ComputedStyle& style) const {
237   return PseudoElementLayoutObjectIsNeeded(&style, this->parentElement());
238 }
239 
CanGeneratePseudoElement(PseudoId pseudo_id) const240 bool PseudoElement::CanGeneratePseudoElement(PseudoId pseudo_id) const {
241   switch (pseudo_id_) {
242     case kPseudoIdBefore:
243     case kPseudoIdAfter:
244       if (pseudo_id != kPseudoIdMarker)
245         return false;
246       break;
247     default:
248       return false;
249   }
250   return Element::CanGeneratePseudoElement(pseudo_id);
251 }
252 
InnerNodeForHitTesting() const253 Node* PseudoElement::InnerNodeForHitTesting() const {
254   Node* parent = ParentOrShadowHostNode();
255   if (parent && parent->IsPseudoElement())
256     return To<PseudoElement>(parent)->InnerNodeForHitTesting();
257   return parent;
258 }
259 
PseudoElementLayoutObjectIsNeeded(const ComputedStyle * pseudo_style,const Element * originating_element)260 bool PseudoElementLayoutObjectIsNeeded(const ComputedStyle* pseudo_style,
261                                        const Element* originating_element) {
262   if (!pseudo_style)
263     return false;
264   if (pseudo_style->Display() == EDisplay::kNone)
265     return false;
266   switch (pseudo_style->StyleType()) {
267     case kPseudoIdFirstLetter:
268     case kPseudoIdBackdrop:
269       return true;
270     case kPseudoIdBefore:
271     case kPseudoIdAfter:
272       return !pseudo_style->ContentPreventsBoxGeneration();
273     case kPseudoIdMarker: {
274       if (!pseudo_style->ContentBehavesAsNormal())
275         return !pseudo_style->ContentPreventsBoxGeneration();
276       const ComputedStyle* parent_style =
277           originating_element->GetComputedStyle();
278       return parent_style &&
279              (parent_style->ListStyleType() != EListStyleType::kNone ||
280               parent_style->GeneratesMarkerImage());
281     }
282     default:
283       NOTREACHED();
284       return false;
285   }
286 }
287 
288 }  // namespace blink
289