| /* |
| * 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<RenderStyle> 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<RenderStyle> RenderDetails::createSummaryStyle() |
| { |
| RefPtr<HTMLElement> 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<HTMLDetailsElement*>(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<Element*>(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 |
| |