1/* 2 * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21#include "config.h" 22#include "HTMLDetailsElement.h" 23 24#if ENABLE(DETAILS_ELEMENT) 25#include "ElementIterator.h" 26#include "HTMLSummaryElement.h" 27#include "InsertionPoint.h" 28#include "LocalizedStrings.h" 29#include "MouseEvent.h" 30#include "RenderBlockFlow.h" 31#include "Text.h" 32 33namespace WebCore { 34 35using namespace HTMLNames; 36 37static const AtomicString& summaryQuerySelector() 38{ 39 DEPRECATED_DEFINE_STATIC_LOCAL(AtomicString, selector, ("summary:first-of-type", AtomicString::ConstructFromLiteral)); 40 return selector; 41}; 42 43class DetailsContentElement : public InsertionPoint { 44public: 45 static PassRefPtr<DetailsContentElement> create(Document&); 46 47private: 48 DetailsContentElement(Document& document) 49 : InsertionPoint(webkitShadowContentTag, document) 50 { 51 } 52 53 virtual MatchType matchTypeFor(Node* node) const override 54 { 55 if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) 56 return NeverMatches; 57 return AlwaysMatches; 58 } 59}; 60 61PassRefPtr<DetailsContentElement> DetailsContentElement::create(Document& document) 62{ 63 return adoptRef(new DetailsContentElement(document)); 64} 65 66class DetailsSummaryElement : public InsertionPoint { 67public: 68 static PassRefPtr<DetailsSummaryElement> create(Document&); 69 70 Element* fallbackSummary() 71 { 72 ASSERT(firstChild() && firstChild()->hasTagName(summaryTag)); 73 return toElement(firstChild()); 74 } 75 76private: 77 DetailsSummaryElement(Document& document) 78 : InsertionPoint(webkitShadowContentTag, document) 79 { 80 } 81 82 virtual MatchType matchTypeFor(Node* node) const override 83 { 84 if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) 85 return AlwaysMatches; 86 return NeverMatches; 87 } 88}; 89 90PassRefPtr<DetailsSummaryElement> DetailsSummaryElement::create(Document& document) 91{ 92 RefPtr<HTMLSummaryElement> summary = HTMLSummaryElement::create(summaryTag, document); 93 summary->appendChild(Text::create(document, defaultDetailsSummaryText()), ASSERT_NO_EXCEPTION); 94 95 RefPtr<DetailsSummaryElement> detailsSummary = adoptRef(new DetailsSummaryElement(document)); 96 detailsSummary->appendChild(summary); 97 return detailsSummary.release(); 98} 99 100PassRefPtr<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document& document) 101{ 102 RefPtr<HTMLDetailsElement> details = adoptRef(new HTMLDetailsElement(tagName, document)); 103 details->ensureUserAgentShadowRoot(); 104 return details.release(); 105} 106 107HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document& document) 108 : HTMLElement(tagName, document) 109 , m_isOpen(false) 110{ 111 ASSERT(hasTagName(detailsTag)); 112} 113 114RenderPtr<RenderElement> HTMLDetailsElement::createElementRenderer(PassRef<RenderStyle> style) 115{ 116 return createRenderer<RenderBlockFlow>(*this, WTF::move(style)); 117} 118 119void HTMLDetailsElement::didAddUserAgentShadowRoot(ShadowRoot* root) 120{ 121 root->appendChild(DetailsSummaryElement::create(document()), ASSERT_NO_EXCEPTION); 122 root->appendChild(DetailsContentElement::create(document()), ASSERT_NO_EXCEPTION); 123} 124 125const Element* HTMLDetailsElement::findMainSummary() const 126{ 127 if (auto summary = childrenOfType<HTMLSummaryElement>(*this).first()) 128 return summary; 129 130 return static_cast<DetailsSummaryElement*>(userAgentShadowRoot()->firstChild())->fallbackSummary(); 131} 132 133void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 134{ 135 if (name == openAttr) { 136 bool oldValue = m_isOpen; 137 m_isOpen = !value.isNull(); 138 if (oldValue != m_isOpen) 139 setNeedsStyleRecalc(ReconstructRenderTree); 140 } else 141 HTMLElement::parseAttribute(name, value); 142} 143 144bool HTMLDetailsElement::childShouldCreateRenderer(const Node& child) const 145{ 146 if (child.isPseudoElement()) 147 return HTMLElement::childShouldCreateRenderer(child); 148 149 if (!hasShadowRootOrActiveInsertionPointParent(child)) 150 return false; 151 152 if (m_isOpen) 153 return HTMLElement::childShouldCreateRenderer(child); 154 155 if (!child.hasTagName(summaryTag)) 156 return false; 157 158 return &child == findMainSummary() && HTMLElement::childShouldCreateRenderer(child); 159} 160 161void HTMLDetailsElement::toggleOpen() 162{ 163 setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom); 164} 165 166} 167 168#endif 169