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 "core/html/HTMLDetailsElement.h" 23 24 #include "bindings/core/v8/ExceptionStatePlaceholder.h" 25 #include "core/CSSPropertyNames.h" 26 #include "core/CSSValueKeywords.h" 27 #include "core/HTMLNames.h" 28 #include "core/dom/ElementTraversal.h" 29 #include "core/dom/Text.h" 30 #include "core/dom/shadow/ShadowRoot.h" 31 #include "core/events/Event.h" 32 #include "core/events/EventSender.h" 33 #include "core/frame/UseCounter.h" 34 #include "core/html/HTMLContentElement.h" 35 #include "core/html/HTMLDivElement.h" 36 #include "core/html/HTMLSummaryElement.h" 37 #include "core/html/shadow/ShadowElementNames.h" 38 #include "core/rendering/RenderBlockFlow.h" 39 #include "platform/text/PlatformLocale.h" 40 41 namespace blink { 42 43 using namespace HTMLNames; 44 45 static DetailsEventSender& detailsToggleEventSender() 46 { 47 DEFINE_STATIC_LOCAL(DetailsEventSender, sharedToggleEventSender, (EventTypeNames::toggle)); 48 return sharedToggleEventSender; 49 } 50 51 PassRefPtrWillBeRawPtr<HTMLDetailsElement> HTMLDetailsElement::create(Document& document) 52 { 53 RefPtrWillBeRawPtr<HTMLDetailsElement> details = adoptRefWillBeNoop(new HTMLDetailsElement(document)); 54 details->ensureUserAgentShadowRoot(); 55 return details.release(); 56 } 57 58 HTMLDetailsElement::HTMLDetailsElement(Document& document) 59 : HTMLElement(detailsTag, document) 60 , m_isOpen(false) 61 { 62 UseCounter::count(document, UseCounter::DetailsElement); 63 } 64 65 HTMLDetailsElement::~HTMLDetailsElement() 66 { 67 detailsToggleEventSender().cancelEvent(this); 68 } 69 70 void HTMLDetailsElement::dispatchPendingEvent(DetailsEventSender* eventSender) 71 { 72 ASSERT_UNUSED(eventSender, eventSender == &detailsToggleEventSender()); 73 dispatchEvent(Event::create(EventTypeNames::toggle)); 74 } 75 76 77 RenderObject* HTMLDetailsElement::createRenderer(RenderStyle*) 78 { 79 return new RenderBlockFlow(this); 80 } 81 82 void HTMLDetailsElement::didAddUserAgentShadowRoot(ShadowRoot& root) 83 { 84 DEFINE_STATIC_LOCAL(const AtomicString, summarySelector, ("summary:first-of-type", AtomicString::ConstructFromLiteral)); 85 86 RefPtrWillBeRawPtr<HTMLSummaryElement> defaultSummary = HTMLSummaryElement::create(document()); 87 defaultSummary->appendChild(Text::create(document(), locale().queryString(blink::WebLocalizedString::DetailsLabel))); 88 89 RefPtrWillBeRawPtr<HTMLContentElement> summary = HTMLContentElement::create(document()); 90 summary->setIdAttribute(ShadowElementNames::detailsSummary()); 91 summary->setAttribute(selectAttr, summarySelector); 92 summary->appendChild(defaultSummary); 93 root.appendChild(summary.release()); 94 95 RefPtrWillBeRawPtr<HTMLDivElement> content = HTMLDivElement::create(document()); 96 content->setIdAttribute(ShadowElementNames::detailsContent()); 97 content->appendChild(HTMLContentElement::create(document())); 98 content->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); 99 root.appendChild(content.release()); 100 } 101 102 Element* HTMLDetailsElement::findMainSummary() const 103 { 104 if (HTMLSummaryElement* summary = Traversal<HTMLSummaryElement>::firstChild(*this)) 105 return summary; 106 107 HTMLContentElement* content = toHTMLContentElement(userAgentShadowRoot()->firstChild()); 108 ASSERT(content->firstChild() && isHTMLSummaryElement(*content->firstChild())); 109 return toElement(content->firstChild()); 110 } 111 112 void HTMLDetailsElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 113 { 114 if (name == openAttr) { 115 bool oldValue = m_isOpen; 116 m_isOpen = !value.isNull(); 117 if (m_isOpen == oldValue) 118 return; 119 120 // Dispatch toggle event asynchronously. 121 detailsToggleEventSender().cancelEvent(this); 122 detailsToggleEventSender().dispatchEventSoon(this); 123 124 Element* content = ensureUserAgentShadowRoot().getElementById(ShadowElementNames::detailsContent()); 125 ASSERT(content); 126 if (m_isOpen) 127 content->removeInlineStyleProperty(CSSPropertyDisplay); 128 else 129 content->setInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); 130 Element* summary = ensureUserAgentShadowRoot().getElementById(ShadowElementNames::detailsSummary()); 131 ASSERT(summary); 132 // FIXME: DetailsMarkerControl's RenderDetailsMarker has no concept of being updated 133 // without recreating it causing a repaint. Instead we should change it so we can tell 134 // it to toggle the open/closed triangle state and avoid reattaching the entire summary. 135 summary->lazyReattachIfAttached(); 136 return; 137 } 138 HTMLElement::parseAttribute(name, value); 139 } 140 141 void HTMLDetailsElement::toggleOpen() 142 { 143 setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom); 144 } 145 146 bool HTMLDetailsElement::isInteractiveContent() const 147 { 148 return true; 149 } 150 151 } 152