| /* |
| * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) |
| * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. |
| * Copyright (C) 2010, 2012 Google Inc. All rights reserved. |
| * |
| * 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 "RenderElement.h" |
| |
| #include "ContentData.h" |
| #include "RenderDeprecatedFlexibleBox.h" |
| #include "RenderFlexibleBox.h" |
| #include "RenderGrid.h" |
| #include "RenderImage.h" |
| #include "RenderImageResourceStyleImage.h" |
| #include "RenderLayer.h" |
| #include "RenderListItem.h" |
| #include "RenderMultiColumnBlock.h" |
| #include "RenderRegion.h" |
| #include "RenderRuby.h" |
| #include "RenderRubyText.h" |
| #include "RenderTableCaption.h" |
| #include "RenderTableCell.h" |
| #include "RenderTableCol.h" |
| #include "RenderTableRow.h" |
| #include "RenderText.h" |
| #include "SVGRenderSupport.h" |
| |
| namespace WebCore { |
| |
| RenderElement::RenderElement(Element* element) |
| : RenderObject(element) |
| { |
| } |
| |
| RenderElement::~RenderElement() |
| { |
| } |
| |
| RenderElement* RenderElement::createFor(Element& element, RenderStyle& style) |
| { |
| Document& document = element.document(); |
| RenderArena& arena = *document.renderArena(); |
| |
| // Minimal support for content properties replacing an entire element. |
| // Works only if we have exactly one piece of content and it's a URL. |
| // Otherwise acts as if we didn't support this feature. |
| const ContentData* contentData = style.contentData(); |
| if (contentData && !contentData->next() && contentData->isImage() && !element.isPseudoElement()) { |
| RenderImage* image = new (arena) RenderImage(&element); |
| // RenderImageResourceStyleImage requires a style being present on the image but we don't want to |
| // trigger a style change now as the node is not fully attached. Moving this code to style change |
| // doesn't make sense as it should be run once at renderer creation. |
| image->setStyleInternal(&style); |
| if (const StyleImage* styleImage = static_cast<const ImageContentData*>(contentData)->image()) { |
| image->setImageResource(RenderImageResourceStyleImage::create(const_cast<StyleImage*>(styleImage))); |
| image->setIsGeneratedContent(); |
| } else |
| image->setImageResource(RenderImageResource::create()); |
| image->setStyleInternal(0); |
| return image; |
| } |
| |
| if (element.hasTagName(HTMLNames::rubyTag)) { |
| if (style.display() == INLINE) |
| return new (arena) RenderRubyAsInline(element); |
| if (style.display() == BLOCK) |
| return new (arena) RenderRubyAsBlock(element); |
| } |
| // treat <rt> as ruby text ONLY if it still has its default treatment of block |
| if (element.hasTagName(HTMLNames::rtTag) && style.display() == BLOCK) |
| return new (arena) RenderRubyText(element); |
| if (document.cssRegionsEnabled() && style.isDisplayRegionType() && !style.regionThread().isEmpty()) |
| return new (arena) RenderRegion(&element, 0); |
| switch (style.display()) { |
| case NONE: |
| return 0; |
| case INLINE: |
| return new (arena) RenderInline(&element); |
| case BLOCK: |
| case INLINE_BLOCK: |
| case RUN_IN: |
| case COMPACT: |
| if ((!style.hasAutoColumnCount() || !style.hasAutoColumnWidth()) && document.regionBasedColumnsEnabled()) |
| return new (arena) RenderMultiColumnBlock(element); |
| return new (arena) RenderBlockFlow(&element); |
| case LIST_ITEM: |
| return new (arena) RenderListItem(element); |
| case TABLE: |
| case INLINE_TABLE: |
| return new (arena) RenderTable(&element); |
| case TABLE_ROW_GROUP: |
| case TABLE_HEADER_GROUP: |
| case TABLE_FOOTER_GROUP: |
| return new (arena) RenderTableSection(&element); |
| case TABLE_ROW: |
| return new (arena) RenderTableRow(&element); |
| case TABLE_COLUMN_GROUP: |
| case TABLE_COLUMN: |
| return new (arena) RenderTableCol(element); |
| case TABLE_CELL: |
| return new (arena) RenderTableCell(&element); |
| case TABLE_CAPTION: |
| return new (arena) RenderTableCaption(element); |
| case BOX: |
| case INLINE_BOX: |
| return new (arena) RenderDeprecatedFlexibleBox(element); |
| case FLEX: |
| case INLINE_FLEX: |
| return new (arena) RenderFlexibleBox(&element); |
| case GRID: |
| case INLINE_GRID: |
| return new (arena) RenderGrid(element); |
| } |
| ASSERT_NOT_REACHED(); |
| return nullptr; |
| } |
| |
| void RenderElement::addChild(RenderObject* newChild, RenderObject* beforeChild) |
| { |
| ASSERT(children()); |
| RenderObjectChildList& children = *this->children(); |
| |
| bool needsTable = false; |
| |
| if (newChild->isRenderTableCol()) { |
| RenderTableCol* newTableColumn = toRenderTableCol(newChild); |
| bool isColumnInColumnGroup = newTableColumn->isTableColumn() && isRenderTableCol(); |
| needsTable = !isTable() && !isColumnInColumnGroup; |
| } else if (newChild->isTableCaption()) |
| needsTable = !isTable(); |
| else if (newChild->isTableSection()) |
| needsTable = !isTable(); |
| else if (newChild->isTableRow()) |
| needsTable = !isTableSection(); |
| else if (newChild->isTableCell()) |
| needsTable = !isTableRow(); |
| |
| if (needsTable) { |
| RenderTable* table; |
| RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children.lastChild(); |
| if (afterChild && afterChild->isAnonymous() && afterChild->isTable() && !afterChild->isBeforeContent()) |
| table = toRenderTable(afterChild); |
| else { |
| table = RenderTable::createAnonymousWithParentRenderer(this); |
| addChild(table, beforeChild); |
| } |
| table->addChild(newChild); |
| } else |
| children.insertChildNode(this, newChild, beforeChild); |
| |
| if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) |
| toRenderText(newChild)->transformText(); |
| |
| // SVG creates renderers for <g display="none">, as SVG requires children of hidden |
| // <g>s to have renderers - at least that's how our implementation works. Consider: |
| // <g display="none"><foreignObject><body style="position: relative">FOO... |
| // - requiresLayer() would return true for the <body>, creating a new RenderLayer |
| // - when the document is painted, both layers are painted. The <body> layer doesn't |
| // know that it's inside a "hidden SVG subtree", and thus paints, even if it shouldn't. |
| // To avoid the problem alltogether, detect early if we're inside a hidden SVG subtree |
| // and stop creating layers at all for these cases - they're not used anyways. |
| if (newChild->hasLayer() && !layerCreationAllowedForSubtree()) |
| toRenderLayerModelObject(newChild)->layer()->removeOnlyThisLayer(); |
| |
| #if ENABLE(SVG) |
| SVGRenderSupport::childAdded(this, newChild); |
| #endif |
| } |
| |
| void RenderElement::removeChild(RenderObject* oldChild) |
| { |
| ASSERT(children()); |
| children()->removeChildNode(this, oldChild); |
| } |
| |
| static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderElement*& newObject, RenderLayer*& beforeChild) |
| { |
| if (obj->hasLayer()) { |
| if (!beforeChild && newObject) { |
| // We need to figure out the layer that follows newObject. We only do |
| // this the first time we find a child layer, and then we update the |
| // pointer values for newObject and beforeChild used by everyone else. |
| beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); |
| newObject = nullptr; |
| } |
| parentLayer->addChild(toRenderLayerModelObject(obj)->layer(), beforeChild); |
| return; |
| } |
| |
| for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) |
| addLayers(curr, parentLayer, newObject, beforeChild); |
| } |
| |
| void RenderElement::addLayers(RenderLayer* parentLayer) |
| { |
| if (!parentLayer) |
| return; |
| |
| RenderElement* renderer = this; |
| RenderLayer* beforeChild = nullptr; |
| WebCore::addLayers(this, parentLayer, renderer, beforeChild); |
| } |
| |
| void RenderElement::removeLayers(RenderLayer* parentLayer) |
| { |
| if (!parentLayer) |
| return; |
| |
| if (hasLayer()) { |
| parentLayer->removeChild(toRenderLayerModelObject(this)->layer()); |
| return; |
| } |
| |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isRenderElement()) |
| toRenderElement(child)->removeLayers(parentLayer); |
| } |
| } |
| |
| void RenderElement::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) |
| { |
| if (!newParent) |
| return; |
| |
| if (hasLayer()) { |
| RenderLayer* layer = toRenderLayerModelObject(this)->layer(); |
| ASSERT(oldParent == layer->parent()); |
| if (oldParent) |
| oldParent->removeChild(layer); |
| newParent->addChild(layer); |
| return; |
| } |
| |
| for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { |
| if (child->isRenderElement()) |
| toRenderElement(child)->moveLayers(oldParent, newParent); |
| } |
| } |
| |
| RenderLayer* RenderElement::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, bool checkParent) |
| { |
| // Error check the parent layer passed in. If it's null, we can't find anything. |
| if (!parentLayer) |
| return 0; |
| |
| // Step 1: If our layer is a child of the desired parent, then return our layer. |
| RenderLayer* ourLayer = hasLayer() ? toRenderLayerModelObject(this)->layer() : nullptr; |
| if (ourLayer && ourLayer->parent() == parentLayer) |
| return ourLayer; |
| |
| // Step 2: If we don't have a layer, or our layer is the desired parent, then descend |
| // into our siblings trying to find the next layer whose parent is the desired parent. |
| if (!ourLayer || ourLayer == parentLayer) { |
| for (RenderObject* child = startPoint ? startPoint->nextSibling() : firstChild(); child; child = child->nextSibling()) { |
| if (!child->isRenderElement()) |
| continue; |
| RenderLayer* nextLayer = toRenderElement(child)->findNextLayer(parentLayer, nullptr, false); |
| if (nextLayer) |
| return nextLayer; |
| } |
| } |
| |
| // Step 3: If our layer is the desired parent layer, then we're finished. We didn't |
| // find anything. |
| if (parentLayer == ourLayer) |
| return nullptr; |
| |
| // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that |
| // follow us to see if we can locate a layer. |
| if (checkParent && parent()) |
| return parent()->findNextLayer(parentLayer, this, true); |
| |
| return nullptr; |
| } |
| |
| bool RenderElement::layerCreationAllowedForSubtree() const |
| { |
| #if ENABLE(SVG) |
| RenderElement* parentRenderer = parent(); |
| while (parentRenderer) { |
| if (parentRenderer->isSVGHiddenContainer()) |
| return false; |
| parentRenderer = parentRenderer->parent(); |
| } |
| #endif |
| |
| return true; |
| } |
| |
| void RenderElement::insertedIntoTree() |
| { |
| RenderObject::insertedIntoTree(); |
| |
| // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children |
| // and don't have a layer attached to ourselves. |
| RenderLayer* layer = nullptr; |
| if (firstChild() || hasLayer()) { |
| layer = parent()->enclosingLayer(); |
| addLayers(layer); |
| } |
| |
| // If |this| is visible but this object was not, tell the layer it has some visible content |
| // that needs to be drawn and layer visibility optimization can't be used |
| if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { |
| if (!layer) |
| layer = parent()->enclosingLayer(); |
| if (layer) |
| layer->setHasVisibleContent(); |
| } |
| } |
| |
| void RenderElement::willBeRemovedFromTree() |
| { |
| // If we remove a visible child from an invisible parent, we don't know the layer visibility any more. |
| RenderLayer* layer = nullptr; |
| if (parent()->style()->visibility() != VISIBLE && style()->visibility() == VISIBLE && !hasLayer()) { |
| if ((layer = parent()->enclosingLayer())) |
| layer->dirtyVisibleContentStatus(); |
| } |
| // Keep our layer hierarchy updated. |
| if (firstChild() || hasLayer()) { |
| if (!layer) |
| layer = parent()->enclosingLayer(); |
| removeLayers(layer); |
| } |
| |
| RenderObject::willBeRemovedFromTree(); |
| } |
| |
| |
| } |