/* * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include "config.h" #include "RenderDetails.h" #include "CSSStyleSelector.h" #include "HTMLDetailsElement.h" #include "HTMLNames.h" #include "LocalizedStrings.h" #include "RenderDetailsMarker.h" #include "RenderTextFragment.h" #include "RenderView.h" namespace WebCore { using namespace HTMLNames; RenderDetails::RenderDetails(Node* node) : RenderBlock(node) , m_summaryBlock(0) , m_contentBlock(0) , m_defaultSummaryBlock(0) , m_defaultSummaryText(0) , m_marker(0) , m_mainSummary(0) { } void RenderDetails::destroy() { if (m_marker) { m_marker->destroy(); m_marker = 0; } RenderBlock::destroy(); } RenderBlock* RenderDetails::summaryBlock() { if (!m_summaryBlock) { m_summaryBlock = createAnonymousBlock(); RenderBlock::addChild(m_summaryBlock, m_contentBlock); } return m_summaryBlock; } RenderBlock* RenderDetails::contentBlock() { if (!m_contentBlock) { m_contentBlock = createAnonymousBlock(); RenderBlock::addChild(m_contentBlock); } return m_contentBlock; } void RenderDetails::addChild(RenderObject* newChild, RenderObject* beforeChild) { if (beforeChild && beforeChild == m_mainSummary) beforeChild = getRenderPosition(m_mainSummary); contentBlock()->addChild(newChild, beforeChild); } void RenderDetails::removeChild(RenderObject* oldChild) { if (oldChild == m_summaryBlock) { RenderBlock::removeChild(oldChild); m_summaryBlock = 0; return; } if (oldChild == m_contentBlock) { RenderBlock::removeChild(oldChild); m_contentBlock = 0; return; } if (oldChild == m_mainSummary && m_summaryBlock) { m_summaryBlock->removeChild(m_mainSummary); return; } if (m_contentBlock) { m_contentBlock->removeChild(oldChild); return; } ASSERT_NOT_REACHED(); } void RenderDetails::setMarkerStyle() { if (m_marker) { RefPtr markerStyle = RenderStyle::create(); markerStyle->inheritFrom(style()); m_marker->setStyle(markerStyle.release()); } } void RenderDetails::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBlock::styleDidChange(diff, oldStyle); if (m_defaultSummaryBlock) { m_defaultSummaryBlock->setStyle(createSummaryStyle()); m_defaultSummaryText->setStyle(m_defaultSummaryBlock->style()); } setMarkerStyle(); // Ensure that if we ended up being inline that we set our replaced flag // so that we're treated like an inline-block. setReplaced(isInline()); } RenderObject* RenderDetails::getRenderPosition(RenderObject* object) { if (!object || !object->node()) return 0; Node* element = object->node()->nextSibling(); while (element && !element->renderer()) element = element->nextSibling(); return element ? element->renderer() : 0; } void RenderDetails::markerDestroyed() { m_marker = 0; } void RenderDetails::summaryDestroyed(RenderObject* summary) { if (summary == m_mainSummary) m_mainSummary = 0; } void RenderDetails::moveSummaryToContents() { if (m_defaultSummaryBlock) { ASSERT(!m_mainSummary); m_defaultSummaryBlock->destroy(); m_defaultSummaryBlock = 0; m_defaultSummaryText = 0; return; } if (!m_mainSummary) return; m_mainSummary->remove(); contentBlock()->addChild(m_mainSummary, getRenderPosition(m_mainSummary)); m_mainSummary = 0; } PassRefPtr RenderDetails::createSummaryStyle() { RefPtr summary(HTMLElement::create(summaryTag, document())); return document()->styleSelector()->styleForElement(summary.get(), style(), true); } void RenderDetails::replaceMainSummary(RenderObject* newSummary) { ASSERT(newSummary); if (m_mainSummary == newSummary) return; moveSummaryToContents(); newSummary->remove(); summaryBlock()->addChild(newSummary); m_mainSummary = newSummary; } void RenderDetails::createDefaultSummary() { if (m_defaultSummaryBlock) return; moveSummaryToContents(); m_defaultSummaryBlock = summaryBlock()->createAnonymousBlock(); m_defaultSummaryBlock->setStyle(createSummaryStyle()); m_defaultSummaryText = new (renderArena()) RenderTextFragment(document(), defaultDetailsSummaryText().impl()); m_defaultSummaryText->setStyle(m_defaultSummaryBlock->style()); m_defaultSummaryBlock->addChild(m_defaultSummaryText); summaryBlock()->addChild(m_defaultSummaryBlock); } void RenderDetails::checkMainSummary() { if (!node() || !node()->hasTagName(detailsTag)) return; Node* mainSummaryNode = static_cast(node())->mainSummary(); if (!mainSummaryNode || !mainSummaryNode->renderer() || mainSummaryNode->renderer()->isFloatingOrPositioned()) createDefaultSummary(); else replaceMainSummary(mainSummaryNode->renderer()); } void RenderDetails::layout() { ASSERT(needsLayout()); checkMainSummary(); ASSERT(m_summaryBlock); if (!m_marker) { m_marker = new (renderArena()) RenderDetailsMarker(this); setMarkerStyle(); } updateMarkerLocation(); RenderBlock::layout(); m_interactiveArea = m_summaryBlock->frameRect(); // FIXME: the following code will not be needed once absoluteToLocal get patched to handle flipped blocks writing modes. switch (style()->writingMode()) { case TopToBottomWritingMode: case LeftToRightWritingMode: break; case RightToLeftWritingMode: { m_interactiveArea.setX(width() - m_interactiveArea.x() - m_interactiveArea.width()); break; } case BottomToTopWritingMode: { m_interactiveArea.setY(height() - m_interactiveArea.y() - m_interactiveArea.height()); break; } } } bool RenderDetails::isOpen() const { return node() && node()->isElementNode() ? !static_cast(node())->getAttribute(openAttr).isNull() : false; } RenderObject* RenderDetails::getParentOfFirstLineBox(RenderBlock* curr) { RenderObject* firstChild = curr->firstChild(); if (!firstChild) return 0; for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { if (currChild == m_marker) continue; if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild))) return curr; if (currChild->isFloating() || currChild->isPositioned()) continue; if (currChild->isTable() || !currChild->isRenderBlock() || (currChild->isBox() && toRenderBox(currChild)->isWritingModeRoot())) break; if (currChild->isDetails()) break; RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild)); if (lineBox) return lineBox; } return 0; } RenderObject* RenderDetails::firstNonMarkerChild(RenderObject* parent) { RenderObject* result = parent->firstChild(); while (result && result->isDetailsMarker()) result = result->nextSibling(); return result; } void RenderDetails::updateMarkerLocation() { // Sanity check the location of our marker. if (m_marker) { RenderObject* markerPar = m_marker->parent(); RenderObject* lineBoxParent = getParentOfFirstLineBox(m_summaryBlock); if (!lineBoxParent) { // If the marker is currently contained inside an anonymous box, // then we are the only item in that anonymous box (since no line box // parent was found). It's ok to just leave the marker where it is // in this case. if (markerPar && markerPar->isAnonymousBlock()) lineBoxParent = markerPar; else lineBoxParent = m_summaryBlock; } if (markerPar != lineBoxParent || m_marker->preferredLogicalWidthsDirty()) { // Removing and adding the marker can trigger repainting in // containers other than ourselves, so we need to disable LayoutState. view()->disableLayoutState(); m_marker->remove(); if (!lineBoxParent) lineBoxParent = m_summaryBlock; lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent)); if (m_marker->preferredLogicalWidthsDirty()) m_marker->computePreferredLogicalWidths(); view()->enableLayoutState(); } } } } // namespace WebCore