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 "HTMLContentElement.h" 26#include "HTMLNames.h" 27#include "HTMLSummaryElement.h" 28#include "LocalizedStrings.h" 29#include "MouseEvent.h" 30#include "NodeRenderingContext.h" 31#include "RenderBlock.h" 32#include "ShadowRoot.h" 33#include "Text.h" 34 35namespace WebCore { 36 37using namespace HTMLNames; 38 39static const AtomicString& summaryQuerySelector() 40{ 41 DEFINE_STATIC_LOCAL(AtomicString, selector, ("summary:first-of-type", AtomicString::ConstructFromLiteral)); 42 return selector; 43}; 44 45class DetailsContentElement : public InsertionPoint { 46public: 47 static PassRefPtr<DetailsContentElement> create(Document*); 48 49private: 50 DetailsContentElement(Document* document) 51 : InsertionPoint(HTMLNames::webkitShadowContentTag, document) 52 { 53 } 54 55 virtual MatchType matchTypeFor(Node* node) const OVERRIDE 56 { 57 if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) 58 return NeverMatches; 59 return AlwaysMatches; 60 } 61}; 62 63PassRefPtr<DetailsContentElement> DetailsContentElement::create(Document* document) 64{ 65 return adoptRef(new DetailsContentElement(document)); 66} 67 68class DetailsSummaryElement : public InsertionPoint { 69public: 70 static PassRefPtr<DetailsSummaryElement> create(Document*); 71 72 Element* fallbackSummary() 73 { 74 ASSERT(firstChild() && firstChild()->hasTagName(summaryTag)); 75 return toElement(firstChild()); 76 } 77 78private: 79 DetailsSummaryElement(Document* document) 80 : InsertionPoint(HTMLNames::webkitShadowContentTag, document) 81 { } 82 83 virtual MatchType matchTypeFor(Node* node) const OVERRIDE 84 { 85 if (node->isElementNode() && node == node->parentNode()->querySelector(summaryQuerySelector(), ASSERT_NO_EXCEPTION)) 86 return AlwaysMatches; 87 return NeverMatches; 88 } 89}; 90 91PassRefPtr<DetailsSummaryElement> DetailsSummaryElement::create(Document* document) 92{ 93 RefPtr<HTMLSummaryElement> summary = HTMLSummaryElement::create(summaryTag, document); 94 summary->appendChild(Text::create(document, defaultDetailsSummaryText()), ASSERT_NO_EXCEPTION); 95 96 RefPtr<DetailsSummaryElement> detailsSummary = adoptRef(new DetailsSummaryElement(document)); 97 detailsSummary->appendChild(summary); 98 return detailsSummary.release(); 99} 100 101PassRefPtr<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document* document) 102{ 103 RefPtr<HTMLDetailsElement> details = adoptRef(new HTMLDetailsElement(tagName, document)); 104 details->ensureUserAgentShadowRoot(); 105 return details.release(); 106} 107 108HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document* document) 109 : HTMLElement(tagName, document) 110 , m_isOpen(false) 111{ 112 ASSERT(hasTagName(detailsTag)); 113} 114 115RenderObject* HTMLDetailsElement::createRenderer(RenderArena* arena, RenderStyle*) 116{ 117 return new (arena) RenderBlock(this); 118} 119 120void HTMLDetailsElement::didAddUserAgentShadowRoot(ShadowRoot* root) 121{ 122 root->appendChild(DetailsSummaryElement::create(document()), ASSERT_NO_EXCEPTION, AttachLazily); 123 root->appendChild(DetailsContentElement::create(document()), ASSERT_NO_EXCEPTION, AttachLazily); 124} 125 126Element* HTMLDetailsElement::findMainSummary() const 127{ 128 for (Node* child = firstChild(); child; child = child->nextSibling()) { 129 if (child->hasTagName(summaryTag)) 130 return toElement(child); 131 } 132 133 return static_cast<DetailsSummaryElement*>(userAgentShadowRoot()->firstChild())->fallbackSummary(); 134} 135 136void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 137{ 138 if (name == openAttr) { 139 bool oldValue = m_isOpen; 140 m_isOpen = !value.isNull(); 141 if (oldValue != m_isOpen) 142 reattachIfAttached(); 143 } else 144 HTMLElement::parseAttribute(name, value); 145} 146 147bool HTMLDetailsElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const 148{ 149 if (childContext.node()->isPseudoElement()) 150 return HTMLElement::childShouldCreateRenderer(childContext); 151 152 if (!childContext.isOnEncapsulationBoundary()) 153 return false; 154 155 if (m_isOpen) 156 return HTMLElement::childShouldCreateRenderer(childContext); 157 158 if (!childContext.node()->hasTagName(summaryTag)) 159 return false; 160 161 return childContext.node() == findMainSummary() && HTMLElement::childShouldCreateRenderer(childContext); 162} 163 164void HTMLDetailsElement::toggleOpen() 165{ 166 setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom); 167} 168 169} 170 171#endif 172