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