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