| /* |
| * Copyright (C) 2012 Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Neither the name of Google Inc. nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "third_party/blink/renderer/core/dom/pseudo_element.h" |
| |
| #include "third_party/blink/renderer/core/dom/element_rare_data.h" |
| #include "third_party/blink/renderer/core/dom/first_letter_pseudo_element.h" |
| #include "third_party/blink/renderer/core/frame/use_counter.h" |
| #include "third_party/blink/renderer/core/layout/generated_children.h" |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/layout/layout_quote.h" |
| #include "third_party/blink/renderer/core/probe/core_probes.h" |
| #include "third_party/blink/renderer/core/style/computed_style.h" |
| #include "third_party/blink/renderer/core/style/content_data.h" |
| |
| namespace blink { |
| |
| PseudoElement* PseudoElement::Create(Element* parent, PseudoId pseudo_id) { |
| if (pseudo_id == kPseudoIdFirstLetter) |
| return FirstLetterPseudoElement::Create(parent); |
| return MakeGarbageCollected<PseudoElement>(parent, pseudo_id); |
| } |
| |
| const QualifiedName& PseudoElementTagName(PseudoId pseudo_id) { |
| switch (pseudo_id) { |
| case kPseudoIdAfter: { |
| DEFINE_STATIC_LOCAL(QualifiedName, after, |
| (g_null_atom, "<pseudo:after>", g_null_atom)); |
| return after; |
| } |
| case kPseudoIdBefore: { |
| DEFINE_STATIC_LOCAL(QualifiedName, before, |
| (g_null_atom, "<pseudo:before>", g_null_atom)); |
| return before; |
| } |
| case kPseudoIdBackdrop: { |
| DEFINE_STATIC_LOCAL(QualifiedName, backdrop, |
| (g_null_atom, "<pseudo:backdrop>", g_null_atom)); |
| return backdrop; |
| } |
| case kPseudoIdFirstLetter: { |
| DEFINE_STATIC_LOCAL(QualifiedName, first_letter, |
| (g_null_atom, "<pseudo:first-letter>", g_null_atom)); |
| return first_letter; |
| } |
| default: |
| NOTREACHED(); |
| } |
| DEFINE_STATIC_LOCAL(QualifiedName, name, |
| (g_null_atom, "<pseudo>", g_null_atom)); |
| return name; |
| } |
| |
| String PseudoElement::PseudoElementNameForEvents(PseudoId pseudo_id) { |
| DEFINE_STATIC_LOCAL(const String, after, ("::after")); |
| DEFINE_STATIC_LOCAL(const String, before, ("::before")); |
| switch (pseudo_id) { |
| case kPseudoIdAfter: |
| return after; |
| case kPseudoIdBefore: |
| return before; |
| default: |
| return g_empty_string; |
| } |
| } |
| |
| PseudoElement::PseudoElement(Element* parent, PseudoId pseudo_id) |
| : Element(PseudoElementTagName(pseudo_id), |
| &parent->GetDocument(), |
| kCreateElement), |
| pseudo_id_(pseudo_id) { |
| DCHECK_NE(pseudo_id, kPseudoIdNone); |
| parent->GetTreeScope().AdoptIfNeeded(*this); |
| SetParentOrShadowHostNode(parent); |
| SetHasCustomStyleCallbacks(); |
| if ((pseudo_id == kPseudoIdBefore || pseudo_id == kPseudoIdAfter) && |
| parent->HasTagName(html_names::kInputTag)) { |
| UseCounter::Count(parent->GetDocument(), |
| WebFeature::kPseudoBeforeAfterForInputElement); |
| } |
| } |
| |
| scoped_refptr<ComputedStyle> PseudoElement::CustomStyleForLayoutObject() { |
| scoped_refptr<ComputedStyle> original_style = |
| ParentOrShadowHostElement()->StyleForPseudoElement( |
| PseudoStyleRequest(pseudo_id_)); |
| if (!original_style || original_style->Display() != EDisplay::kContents) |
| return original_style; |
| |
| return StoreOriginalAndReturnLayoutStyle(std::move(original_style)); |
| } |
| |
| scoped_refptr<ComputedStyle> PseudoElement::StoreOriginalAndReturnLayoutStyle( |
| scoped_refptr<ComputedStyle> original_style) { |
| // For display:contents we should not generate a box, but we generate a non- |
| // observable inline box for pseudo elements to be able to locate the |
| // anonymous layout objects for generated content during DetachLayoutTree(). |
| scoped_refptr<ComputedStyle> layout_style = ComputedStyle::Create(); |
| layout_style->InheritFrom(*original_style); |
| layout_style->SetContent(original_style->GetContentData()); |
| layout_style->SetDisplay(EDisplay::kInline); |
| layout_style->SetStyleType(pseudo_id_); |
| |
| // Store the actual ComputedStyle to be able to return the correct values from |
| // getComputedStyle(). |
| StoreNonLayoutObjectComputedStyle(std::move(original_style)); |
| return layout_style; |
| } |
| |
| void PseudoElement::Dispose() { |
| DCHECK(ParentOrShadowHostElement()); |
| |
| probe::pseudoElementDestroyed(this); |
| |
| DCHECK(!nextSibling()); |
| DCHECK(!previousSibling()); |
| |
| DetachLayoutTree(); |
| Element* parent = ParentOrShadowHostElement(); |
| GetDocument().AdoptIfNeeded(*this); |
| SetParentOrShadowHostNode(nullptr); |
| RemovedFrom(*parent); |
| } |
| |
| void PseudoElement::AttachLayoutTree(AttachContext& context) { |
| DCHECK(!GetLayoutObject()); |
| |
| Element::AttachLayoutTree(context); |
| |
| LayoutObject* layout_object = GetLayoutObject(); |
| if (!layout_object) |
| return; |
| |
| // This is to ensure that bypassing the CanHaveGeneratedChildren() check in |
| // LayoutTreeBuilderForElement::ShouldCreateLayoutObject() does not result in |
| // the backdrop pseudo element's layout object becoming the child of a layout |
| // object that doesn't allow children. |
| DCHECK(layout_object->Parent()); |
| DCHECK(CanHaveGeneratedChildren(*layout_object->Parent())); |
| |
| ComputedStyle& style = layout_object->MutableStyleRef(); |
| if (style.StyleType() != kPseudoIdBefore && |
| style.StyleType() != kPseudoIdAfter) |
| return; |
| DCHECK(style.GetContentData()); |
| |
| for (const ContentData* content = style.GetContentData(); content; |
| content = content->Next()) { |
| LayoutObject* child = content->CreateLayoutObject(*this, style); |
| if (layout_object->IsChildAllowed(child, style)) { |
| layout_object->AddChild(child); |
| if (child->IsQuote()) |
| ToLayoutQuote(child)->AttachQuote(); |
| } else { |
| child->Destroy(); |
| } |
| } |
| } |
| |
| bool PseudoElement::LayoutObjectIsNeeded(const ComputedStyle& style) const { |
| return PseudoElementLayoutObjectIsNeeded(&style); |
| } |
| |
| Node* PseudoElement::InnerNodeForHitTesting() const { |
| return ParentOrShadowHostNode(); |
| } |
| |
| const ComputedStyle* PseudoElement::VirtualEnsureComputedStyle( |
| PseudoId pseudo_element_specifier) { |
| if (HasRareData()) { |
| // Prefer NonLayoutObjectComputedStyle() for display:contents pseudos |
| // instead of the ComputedStyle for the fictional inline box (see |
| // CustomStyleForLayoutObject). |
| if (const ComputedStyle* non_layout_computed_style = |
| NonLayoutObjectComputedStyle()) { |
| DCHECK(!GetLayoutObject() || |
| non_layout_computed_style->Display() == EDisplay::kContents); |
| return non_layout_computed_style; |
| } |
| } |
| return EnsureComputedStyle(pseudo_element_specifier); |
| } |
| |
| bool PseudoElementLayoutObjectIsNeeded(const ComputedStyle* style) { |
| if (!style) |
| return false; |
| if (style->Display() == EDisplay::kNone) |
| return false; |
| if (style->StyleType() == kPseudoIdFirstLetter || |
| style->StyleType() == kPseudoIdBackdrop) |
| return true; |
| return style->GetContentData(); |
| } |
| |
| } // namespace blink |