Index: third_party/WebKit/WebCore/rendering/RenderObject.cpp |
=================================================================== |
--- third_party/WebKit/WebCore/rendering/RenderObject.cpp (revision 9383) |
+++ third_party/WebKit/WebCore/rendering/RenderObject.cpp (working copy) |
@@ -1,2824 +1,2827 @@ |
-/* |
- * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
- * (C) 1999 Antti Koivisto (koivisto@kde.org) |
- * (C) 2000 Dirk Mueller (mueller@kde.org) |
- * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) |
- * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple 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 "RenderObject.h" |
- |
-#include "AXObjectCache.h" |
-#include "CSSStyleSelector.h" |
-#include "FloatQuad.h" |
-#include "FrameView.h" |
-#include "GraphicsContext.h" |
-#include "HTMLNames.h" |
-#include "HitTestResult.h" |
-#include "Page.h" |
-#include "RenderArena.h" |
-#include "RenderCounter.h" |
-#include "RenderFlexibleBox.h" |
-#include "RenderImageGeneratedContent.h" |
-#include "RenderInline.h" |
-#include "RenderListItem.h" |
-#include "RenderTableCell.h" |
-#include "RenderTableCol.h" |
-#include "RenderTableRow.h" |
-#include "RenderTheme.h" |
-#include "RenderView.h" |
-#include <algorithm> |
-#include <stdio.h> |
-#include <wtf/RefCountedLeakCounter.h> |
- |
-#if ENABLE(WML) |
-#include "WMLNames.h" |
-#endif |
- |
-using namespace std; |
- |
-namespace WebCore { |
- |
-using namespace HTMLNames; |
- |
-#ifndef NDEBUG |
-static void* baseOfRenderObjectBeingDeleted; |
-#endif |
- |
-bool RenderObject::s_affectsParentBlock = false; |
- |
-void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw() |
-{ |
- return renderArena->allocate(sz); |
-} |
- |
-void RenderObject::operator delete(void* ptr, size_t sz) |
-{ |
- ASSERT(baseOfRenderObjectBeingDeleted == ptr); |
- |
- // Stash size where destroy can find it. |
- *(size_t *)ptr = sz; |
-} |
- |
-RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) |
-{ |
- Document* doc = node->document(); |
- RenderArena* arena = doc->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->m_next && contentData->m_type == CONTENT_OBJECT && doc != node) { |
- RenderImageGeneratedContent* image = new (arena) RenderImageGeneratedContent(node); |
- image->setStyle(style); |
- if (StyleImage* styleImage = contentData->m_content.m_image) |
- image->setStyleImage(styleImage); |
- return image; |
- } |
- |
- RenderObject* o = 0; |
- |
- switch (style->display()) { |
- case NONE: |
- break; |
- case INLINE: |
- o = new (arena) RenderInline(node); |
- break; |
- case BLOCK: |
- o = new (arena) RenderBlock(node); |
- break; |
- case INLINE_BLOCK: |
- o = new (arena) RenderBlock(node); |
- break; |
- case LIST_ITEM: |
- o = new (arena) RenderListItem(node); |
- break; |
- case RUN_IN: |
- case COMPACT: |
- o = new (arena) RenderBlock(node); |
- break; |
- case TABLE: |
- case INLINE_TABLE: |
- o = new (arena) RenderTable(node); |
- break; |
- case TABLE_ROW_GROUP: |
- case TABLE_HEADER_GROUP: |
- case TABLE_FOOTER_GROUP: |
- o = new (arena) RenderTableSection(node); |
- break; |
- case TABLE_ROW: |
- o = new (arena) RenderTableRow(node); |
- break; |
- case TABLE_COLUMN_GROUP: |
- case TABLE_COLUMN: |
- o = new (arena) RenderTableCol(node); |
- break; |
- case TABLE_CELL: |
- o = new (arena) RenderTableCell(node); |
- break; |
- case TABLE_CAPTION: |
- o = new (arena) RenderBlock(node); |
- break; |
- case BOX: |
- case INLINE_BOX: |
- o = new (arena) RenderFlexibleBox(node); |
- break; |
- } |
- |
- return o; |
-} |
- |
-#ifndef NDEBUG |
-static WTF::RefCountedLeakCounter renderObjectCounter("RenderObject"); |
-#endif |
- |
-RenderObject::RenderObject(Node* node) |
- : CachedResourceClient() |
- , m_style(0) |
- , m_node(node) |
- , m_parent(0) |
- , m_previous(0) |
- , m_next(0) |
-#ifndef NDEBUG |
- , m_hasAXObject(false) |
- , m_setNeedsLayoutForbidden(false) |
-#endif |
- , m_verticalPosition(PositionUndefined) |
- , m_needsLayout(false) |
- , m_needsPositionedMovementLayout(false) |
- , m_normalChildNeedsLayout(false) |
- , m_posChildNeedsLayout(false) |
- , m_prefWidthsDirty(false) |
- , m_floating(false) |
- , m_positioned(false) |
- , m_relPositioned(false) |
- , m_paintBackground(false) |
- , m_isAnonymous(node == node->document()) |
- , m_isText(false) |
- , m_isBox(false) |
- , m_inline(true) |
- , m_replaced(false) |
- , m_isDragging(false) |
- , m_hasLayer(false) |
- , m_hasOverflowClip(false) |
- , m_hasTransform(false) |
- , m_hasReflection(false) |
- , m_hasOverrideSize(false) |
- , m_hasCounterNodeMap(false) |
- , m_everHadLayout(false) |
- , m_childrenInline(false) |
- , m_topMarginQuirk(false) |
- , m_bottomMarginQuirk(false) |
- , m_hasMarkupTruncation(false) |
- , m_selectionState(SelectionNone) |
- , m_hasColumns(false) |
- , m_cellWidthChanged(false) |
-{ |
-#ifndef NDEBUG |
- renderObjectCounter.increment(); |
-#endif |
-} |
- |
-RenderObject::~RenderObject() |
-{ |
- ASSERT(!node() || documentBeingDestroyed() || !document()->frame()->view() || document()->frame()->view()->layoutRoot() != this); |
-#ifndef NDEBUG |
- ASSERT(!m_hasAXObject); |
- renderObjectCounter.decrement(); |
-#endif |
-} |
- |
-bool RenderObject::isDescendantOf(const RenderObject* obj) const |
-{ |
- for (const RenderObject* r = this; r; r = r->m_parent) { |
- if (r == obj) |
- return true; |
- } |
- return false; |
-} |
- |
-bool RenderObject::isBody() const |
-{ |
- return node()->hasTagName(bodyTag); |
-} |
- |
-bool RenderObject::isHR() const |
-{ |
- return element() && element()->hasTagName(hrTag); |
-} |
- |
-bool RenderObject::isHTMLMarquee() const |
-{ |
- return element() && element()->renderer() == this && element()->hasTagName(marqueeTag); |
-} |
- |
- |
-static void updateListMarkerNumbers(RenderObject* child) |
-{ |
- for (RenderObject* r = child; r; r = r->nextSibling()) |
- if (r->isListItem()) |
- static_cast<RenderListItem*>(r)->updateValue(); |
-} |
- |
-void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) |
-{ |
- RenderObjectChildList* children = virtualChildren(); |
- ASSERT(children); |
- if (!children) |
- return; |
- |
- bool needsTable = false; |
- |
- if (newChild->isListItem()) |
- updateListMarkerNumbers(beforeChild ? beforeChild : children->lastChild()); |
- else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) |
- needsTable = !isTable(); |
- else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) |
- needsTable = !isTable(); |
- else if (newChild->isTableSection()) |
- needsTable = !isTable(); |
- else if (newChild->isTableRow()) |
- needsTable = !isTableSection(); |
- else if (newChild->isTableCell()) { |
- needsTable = !isTableRow(); |
- // I'm not 100% sure this is the best way to fix this, but without this |
- // change we recurse infinitely when trying to render the CSS2 test page: |
- // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. |
- // See Radar 2925291. |
- if (needsTable && isTableCell() && !children->firstChild() && !newChild->isTableCell()) |
- needsTable = false; |
- } |
- |
- if (needsTable) { |
- RenderTable* table; |
- RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild(); |
- if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) |
- table = static_cast<RenderTable*>(afterChild); |
- else { |
- table = new (renderArena()) RenderTable(document() /* is anonymous */); |
- RefPtr<RenderStyle> newStyle = RenderStyle::create(); |
- newStyle->inheritFrom(style()); |
- newStyle->setDisplay(TABLE); |
- table->setStyle(newStyle.release()); |
- addChild(table, beforeChild); |
- } |
- table->addChild(newChild); |
- } else { |
- // Just add it... |
- children->insertChildNode(this, newChild, beforeChild); |
- } |
- |
- if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { |
- RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); |
- if (textToTransform) |
- toRenderText(newChild)->setText(textToTransform.release(), true); |
- } |
-} |
- |
-void RenderObject::removeChild(RenderObject* oldChild) |
-{ |
- RenderObjectChildList* children = virtualChildren(); |
- ASSERT(children); |
- if (!children) |
- return; |
- |
- // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode |
- // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on |
- // layout anyway). |
- if (oldChild->isFloatingOrPositioned()) |
- toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists(); |
- |
- children->removeChildNode(this, oldChild); |
-} |
- |
-RenderObject* RenderObject::nextInPreOrder() const |
-{ |
- if (RenderObject* o = firstChild()) |
- return o; |
- |
- return nextInPreOrderAfterChildren(); |
-} |
- |
-RenderObject* RenderObject::nextInPreOrderAfterChildren() const |
-{ |
- RenderObject* o; |
- if (!(o = nextSibling())) { |
- o = parent(); |
- while (o && !o->nextSibling()) |
- o = o->parent(); |
- if (o) |
- o = o->nextSibling(); |
- } |
- |
- return o; |
-} |
- |
-RenderObject* RenderObject::nextInPreOrder(RenderObject* stayWithin) const |
-{ |
- if (RenderObject* o = firstChild()) |
- return o; |
- |
- return nextInPreOrderAfterChildren(stayWithin); |
-} |
- |
-RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin) const |
-{ |
- if (this == stayWithin) |
- return 0; |
- |
- RenderObject* o; |
- if (!(o = nextSibling())) { |
- o = parent(); |
- while (o && !o->nextSibling()) { |
- if (o == stayWithin) |
- return 0; |
- o = o->parent(); |
- } |
- if (o) |
- o = o->nextSibling(); |
- } |
- |
- return o; |
-} |
- |
-RenderObject* RenderObject::previousInPreOrder() const |
-{ |
- if (RenderObject* o = previousSibling()) { |
- while (o->lastChild()) |
- o = o->lastChild(); |
- return o; |
- } |
- |
- return parent(); |
-} |
- |
-RenderObject* RenderObject::childAt(unsigned index) const |
-{ |
- RenderObject* child = firstChild(); |
- for (unsigned i = 0; child && i < index; i++) |
- child = child->nextSibling(); |
- return child; |
-} |
- |
-bool RenderObject::isEditable() const |
-{ |
- RenderText* textRenderer = 0; |
- if (isText()) |
- textRenderer = toRenderText(const_cast<RenderObject*>(this)); |
- |
- return style()->visibility() == VISIBLE && |
- element() && element()->isContentEditable() && |
- ((isBlockFlow() && !firstChild()) || |
- isReplaced() || |
- isBR() || |
- (textRenderer && textRenderer->firstTextBox())); |
-} |
- |
-RenderObject* RenderObject::firstLeafChild() const |
-{ |
- RenderObject* r = firstChild(); |
- while (r) { |
- RenderObject* n = 0; |
- n = r->firstChild(); |
- if (!n) |
- break; |
- r = n; |
- } |
- return r; |
-} |
- |
-RenderObject* RenderObject::lastLeafChild() const |
-{ |
- RenderObject* r = lastChild(); |
- while (r) { |
- RenderObject* n = 0; |
- n = r->lastChild(); |
- if (!n) |
- break; |
- r = n; |
- } |
- return r; |
-} |
- |
-static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& 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 = 0; |
- } |
- parentLayer->addChild(toRenderBox(obj)->layer(), beforeChild); |
- return; |
- } |
- |
- for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) |
- addLayers(curr, parentLayer, newObject, beforeChild); |
-} |
- |
-void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject) |
-{ |
- if (!parentLayer) |
- return; |
- |
- RenderObject* object = newObject; |
- RenderLayer* beforeChild = 0; |
- WebCore::addLayers(this, parentLayer, object, beforeChild); |
-} |
- |
-void RenderObject::removeLayers(RenderLayer* parentLayer) |
-{ |
- if (!parentLayer) |
- return; |
- |
- if (hasLayer()) { |
- parentLayer->removeChild(toRenderBox(this)->layer()); |
- return; |
- } |
- |
- for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
- curr->removeLayers(parentLayer); |
-} |
- |
-void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) |
-{ |
- if (!newParent) |
- return; |
- |
- if (hasLayer()) { |
- RenderLayer* layer = toRenderBox(this)->layer(); |
- if (oldParent) |
- oldParent->removeChild(layer); |
- newParent->addChild(layer); |
- return; |
- } |
- |
- for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
- curr->moveLayers(oldParent, newParent); |
-} |
- |
-RenderLayer* RenderObject::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() ? toRenderBox(this)->layer() : 0; |
- 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* curr = startPoint ? startPoint->nextSibling() : firstChild(); |
- curr; curr = curr->nextSibling()) { |
- RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, 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 0; |
- |
- // 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 0; |
-} |
- |
-RenderLayer* RenderObject::enclosingLayer() const |
-{ |
- const RenderObject* curr = this; |
- while (curr) { |
- RenderLayer* layer = curr->hasLayer() ? toRenderBox(curr)->layer() : 0; |
- if (layer) |
- return layer; |
- curr = curr->parent(); |
- } |
- return 0; |
-} |
- |
-#if USE(ACCELERATED_COMPOSITING) |
-RenderLayer* RenderObject::enclosingCompositingLayer() const |
-{ |
- const RenderObject* curr = this; |
- while (curr) { |
- RenderLayer* layer = curr->hasLayer() ? toRenderBox(curr)->layer() : 0; |
- if (layer && layer->isComposited()) |
- return layer; |
- curr = curr->parent(); |
- } |
- return 0; |
-} |
-#endif |
- |
-RenderBox* RenderObject::enclosingBox() const |
-{ |
- RenderObject* curr = const_cast<RenderObject*>(this); |
- while (curr) { |
- if (curr->isBox()) |
- return toRenderBox(curr); |
- curr = curr->parent(); |
- } |
- |
- ASSERT_NOT_REACHED(); |
- return 0; |
-} |
- |
-RenderBlock* RenderObject::firstLineBlock() const |
-{ |
- return 0; |
-} |
- |
-void RenderObject::setPrefWidthsDirty(bool b, bool markParents) |
-{ |
- bool alreadyDirty = m_prefWidthsDirty; |
- m_prefWidthsDirty = b; |
- if (b && !alreadyDirty && markParents && (isText() || (style()->position() != FixedPosition && style()->position() != AbsolutePosition))) |
- invalidateContainerPrefWidths(); |
-} |
- |
-void RenderObject::invalidateContainerPrefWidths() |
-{ |
- // In order to avoid pathological behavior when inlines are deeply nested, we do include them |
- // in the chain that we mark dirty (even though they're kind of irrelevant). |
- RenderObject* o = isTableCell() ? containingBlock() : container(); |
- while (o && !o->m_prefWidthsDirty) { |
- o->m_prefWidthsDirty = true; |
- if (o->style()->position() == FixedPosition || o->style()->position() == AbsolutePosition) |
- // A positioned object has no effect on the min/max width of its containing block ever. |
- // We can optimize this case and not go up any further. |
- break; |
- o = o->isTableCell() ? o->containingBlock() : o->container(); |
- } |
-} |
- |
-void RenderObject::setLayerNeedsFullRepaint() |
-{ |
- toRenderBox(this)->layer()->setNeedsFullRepaint(true); |
-} |
- |
-RenderBlock* RenderObject::containingBlock() const |
-{ |
- if (isTableCell()) { |
- const RenderTableCell* cell = static_cast<const RenderTableCell*>(this); |
- if (parent() && cell->section()) |
- return cell->table(); |
- return 0; |
- } |
- |
- if (isRenderView()) |
- return const_cast<RenderView*>(toRenderView(this)); |
- |
- RenderObject* o = parent(); |
- if (!isText() && m_style->position() == FixedPosition) { |
- while (o && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) |
- o = o->parent(); |
- } else if (!isText() && m_style->position() == AbsolutePosition) { |
- while (o && (o->style()->position() == StaticPosition || (o->isInline() && !o->isReplaced())) && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { |
- // For relpositioned inlines, we return the nearest enclosing block. We don't try |
- // to return the inline itself. This allows us to avoid having a positioned objects |
- // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result |
- // from this method. The container() method can actually be used to obtain the |
- // inline directly. |
- if (o->style()->position() == RelativePosition && o->isInline() && !o->isReplaced()) |
- return o->containingBlock(); |
- o = o->parent(); |
- } |
- } else { |
- while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection() |
- || o->isTableCol() || o->isFrameSet() || o->isMedia() |
-#if ENABLE(SVG) |
- || o->isSVGContainer() || o->isSVGRoot() |
-#endif |
- )) |
- o = o->parent(); |
- } |
- |
- if (!o || !o->isRenderBlock()) |
- return 0; // Probably doesn't happen any more, but leave just in case. -dwh |
- |
- return toRenderBlock(o); |
-} |
- |
-int RenderObject::containingBlockWidth() const |
-{ |
- return containingBlock()->availableWidth(); |
-} |
- |
-int RenderObject::containingBlockHeight() const |
-{ |
- return containingBlock()->contentHeight(); |
-} |
- |
-static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) |
-{ |
- // Nobody will use multiple layers without wanting fancy positioning. |
- if (layer->next()) |
- return true; |
- |
- // Make sure we have a valid image. |
- StyleImage* img = layer->image(); |
- bool shouldPaintBackgroundImage = img && img->canRender(renderer->style()->effectiveZoom()); |
- |
- // These are always percents or auto. |
- if (shouldPaintBackgroundImage && |
- (!layer->xPosition().isZero() || !layer->yPosition().isZero() || |
- layer->size().width().isPercent() || layer->size().height().isPercent())) |
- // The image will shift unpredictably if the size changes. |
- return true; |
- |
- return false; |
-} |
- |
-bool RenderObject::mustRepaintBackgroundOrBorder() const |
-{ |
- if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers())) |
- return true; |
- |
- // If we don't have a background/border/mask, then nothing to do. |
- if (!hasBoxDecorations()) |
- return false; |
- |
- if (mustRepaintFillLayers(this, style()->backgroundLayers())) |
- return true; |
- |
- // Our fill layers are ok. Let's check border. |
- if (style()->hasBorder()) { |
- // Border images are not ok. |
- StyleImage* borderImage = style()->borderImage().image(); |
- bool shouldPaintBorderImage = borderImage && borderImage->canRender(style()->effectiveZoom()); |
- |
- // If the image hasn't loaded, we're still using the normal border style. |
- if (shouldPaintBorderImage && borderImage->isLoaded()) |
- return true; |
- } |
- |
- return false; |
-} |
- |
-void RenderObject::drawBorderArc(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, |
- int angleStart, int angleSpan, BorderSide s, Color c, const Color& textColor, |
- EBorderStyle style, bool firstCorner) |
-{ |
- if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2)) |
- style = SOLID; |
- |
- if (!c.isValid()) { |
- if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) |
- c.setRGB(238, 238, 238); |
- else |
- c = textColor; |
- } |
- |
- switch (style) { |
- case BNONE: |
- case BHIDDEN: |
- return; |
- case DOTTED: |
- case DASHED: |
- graphicsContext->setStrokeColor(c); |
- graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); |
- graphicsContext->setStrokeThickness(thickness); |
- graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
- break; |
- case DOUBLE: { |
- float third = thickness / 3.0f; |
- float innerThird = (thickness + 1.0f) / 6.0f; |
- int shiftForInner = static_cast<int>(innerThird * 2.5f); |
- |
- int outerY = y; |
- int outerHeight = radius.height() * 2; |
- int innerX = x + shiftForInner; |
- int innerY = y + shiftForInner; |
- int innerWidth = (radius.width() - shiftForInner) * 2; |
- int innerHeight = (radius.height() - shiftForInner) * 2; |
- if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) { |
- outerHeight += 2; |
- innerHeight += 2; |
- } |
- |
- graphicsContext->setStrokeStyle(SolidStroke); |
- graphicsContext->setStrokeColor(c); |
- graphicsContext->setStrokeThickness(third); |
- graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); |
- graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); |
- graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan); |
- break; |
- } |
- case GROOVE: |
- case RIDGE: { |
- Color c2; |
- if ((style == RIDGE && (s == BSTop || s == BSLeft)) || |
- (style == GROOVE && (s == BSBottom || s == BSRight))) |
- c2 = c.dark(); |
- else { |
- c2 = c; |
- c = c.dark(); |
- } |
- |
- graphicsContext->setStrokeStyle(SolidStroke); |
- graphicsContext->setStrokeColor(c); |
- graphicsContext->setStrokeThickness(thickness); |
- graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
- |
- float halfThickness = (thickness + 1.0f) / 4.0f; |
- int shiftForInner = static_cast<int>(halfThickness * 1.5f); |
- graphicsContext->setStrokeColor(c2); |
- graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); |
- graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, |
- (radius.height() - shiftForInner) * 2), angleStart, angleSpan); |
- break; |
- } |
- case INSET: |
- if (s == BSTop || s == BSLeft) |
- c = c.dark(); |
- case OUTSET: |
- if (style == OUTSET && (s == BSBottom || s == BSRight)) |
- c = c.dark(); |
- case SOLID: |
- graphicsContext->setStrokeStyle(SolidStroke); |
- graphicsContext->setStrokeColor(c); |
- graphicsContext->setStrokeThickness(thickness); |
- graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
- break; |
- } |
-} |
- |
-void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, |
- BorderSide s, Color c, const Color& textcolor, EBorderStyle style, |
- int adjbw1, int adjbw2) |
-{ |
- int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1); |
- |
- if (style == DOUBLE && width < 3) |
- style = SOLID; |
- |
- if (!c.isValid()) { |
- if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) |
- c.setRGB(238, 238, 238); |
- else |
- c = textcolor; |
- } |
- |
- switch (style) { |
- case BNONE: |
- case BHIDDEN: |
- return; |
- case DOTTED: |
- case DASHED: |
- graphicsContext->setStrokeColor(c); |
- graphicsContext->setStrokeThickness(width); |
- graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); |
- |
- if (width > 0) |
- switch (s) { |
- case BSBottom: |
- case BSTop: |
- graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); |
- break; |
- case BSRight: |
- case BSLeft: |
- graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); |
- break; |
- } |
- break; |
- case DOUBLE: { |
- int third = (width + 1) / 3; |
- |
- if (adjbw1 == 0 && adjbw2 == 0) { |
- graphicsContext->setStrokeStyle(NoStroke); |
- graphicsContext->setFillColor(c); |
- switch (s) { |
- case BSTop: |
- case BSBottom: |
- graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, third)); |
- graphicsContext->drawRect(IntRect(x1, y2 - third, x2 - x1, third)); |
- break; |
- case BSLeft: |
- graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); |
- graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); |
- break; |
- case BSRight: |
- graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); |
- graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); |
- break; |
- } |
- } else { |
- int adjbw1bigthird = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 3; |
- int adjbw2bigthird = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 3; |
- |
- switch (s) { |
- case BSTop: |
- drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), |
- y1, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y1 + third, |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), |
- y2 - third, x2 - max((adjbw2 * 2 + 1) / 3, 0), y2, |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- break; |
- case BSLeft: |
- drawBorder(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0), |
- x1 + third, y2 - max((-adjbw2 * 2 + 1) / 3, 0), |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- drawBorder(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0), |
- x2, y2 - max((adjbw2 * 2 + 1) / 3, 0), |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- break; |
- case BSBottom: |
- drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), |
- y1, x2 - max((adjbw2 * 2 + 1) / 3, 0), y1 + third, |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), |
- y2 - third, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y2, |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- break; |
- case BSRight: |
- drawBorder(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0), |
- x1 + third, y2 - max(( adjbw2 * 2 + 1) / 3, 0), |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- drawBorder(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0), |
- x2, y2 - max((-adjbw2 * 2 + 1) / 3, 0), |
- s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
- break; |
- default: |
- break; |
- } |
- } |
- break; |
- } |
- case RIDGE: |
- case GROOVE: |
- { |
- EBorderStyle s1; |
- EBorderStyle s2; |
- if (style == GROOVE) { |
- s1 = INSET; |
- s2 = OUTSET; |
- } else { |
- s1 = OUTSET; |
- s2 = INSET; |
- } |
- |
- int adjbw1bighalf = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 2; |
- int adjbw2bighalf = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 2; |
- |
- switch (s) { |
- case BSTop: |
- drawBorder(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, |
- s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); |
- drawBorder(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2, |
- s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); |
- break; |
- case BSLeft: |
- drawBorder(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2, |
- s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); |
- drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2, |
- s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); |
- break; |
- case BSBottom: |
- drawBorder(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, |
- s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); |
- drawBorder(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2, |
- s, c, textcolor, s1, adjbw1/2, adjbw2/2); |
- break; |
- case BSRight: |
- drawBorder(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2, |
- s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); |
- drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2, |
- s, c, textcolor, s1, adjbw1/2, adjbw2/2); |
- break; |
- } |
- break; |
- } |
- case INSET: |
- if (s == BSTop || s == BSLeft) |
- c = c.dark(); |
- // fall through |
- case OUTSET: |
- if (style == OUTSET && (s == BSBottom || s == BSRight)) |
- c = c.dark(); |
- // fall through |
- case SOLID: { |
- graphicsContext->setStrokeStyle(NoStroke); |
- graphicsContext->setFillColor(c); |
- ASSERT(x2 >= x1); |
- ASSERT(y2 >= y1); |
- if (!adjbw1 && !adjbw2) { |
- graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); |
- return; |
- } |
- FloatPoint quad[4]; |
- switch (s) { |
- case BSTop: |
- quad[0] = FloatPoint(x1 + max(-adjbw1, 0), y1); |
- quad[1] = FloatPoint(x1 + max(adjbw1, 0), y2); |
- quad[2] = FloatPoint(x2 - max(adjbw2, 0), y2); |
- quad[3] = FloatPoint(x2 - max(-adjbw2, 0), y1); |
- break; |
- case BSBottom: |
- quad[0] = FloatPoint(x1 + max(adjbw1, 0), y1); |
- quad[1] = FloatPoint(x1 + max(-adjbw1, 0), y2); |
- quad[2] = FloatPoint(x2 - max(-adjbw2, 0), y2); |
- quad[3] = FloatPoint(x2 - max(adjbw2, 0), y1); |
- break; |
- case BSLeft: |
- quad[0] = FloatPoint(x1, y1 + max(-adjbw1, 0)); |
- quad[1] = FloatPoint(x1, y2 - max(-adjbw2, 0)); |
- quad[2] = FloatPoint(x2, y2 - max(adjbw2, 0)); |
- quad[3] = FloatPoint(x2, y1 + max(adjbw1, 0)); |
- break; |
- case BSRight: |
- quad[0] = FloatPoint(x1, y1 + max(adjbw1, 0)); |
- quad[1] = FloatPoint(x1, y2 - max(adjbw2, 0)); |
- quad[2] = FloatPoint(x2, y2 - max(-adjbw2, 0)); |
- quad[3] = FloatPoint(x2, y1 + max(-adjbw1, 0)); |
- break; |
- } |
- graphicsContext->drawConvexPolygon(4, quad); |
- break; |
- } |
- } |
-} |
- |
-bool RenderObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, |
- const NinePieceImage& ninePieceImage, CompositeOperator op) |
-{ |
- StyleImage* styleImage = ninePieceImage.image(); |
- if (!styleImage || !styleImage->canRender(style->effectiveZoom())) |
- return false; |
- |
- if (!styleImage->isLoaded()) |
- return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. |
- |
- // If we have a border radius, the image gets clipped to the rounded rect. |
- bool clipped = false; |
- if (style->hasBorderRadius()) { |
- IntRect clipRect(tx, ty, w, h); |
- graphicsContext->save(); |
- graphicsContext->addRoundedRectClip(clipRect, style->borderTopLeftRadius(), style->borderTopRightRadius(), |
- style->borderBottomLeftRadius(), style->borderBottomRightRadius()); |
- clipped = true; |
- } |
- |
- // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function |
- // doesn't have any understanding of the zoom that is in effect on the tile. |
- styleImage->setImageContainerSize(IntSize(w, h)); |
- IntSize imageSize = styleImage->imageSize(this, 1.0f); |
- int imageWidth = imageSize.width(); |
- int imageHeight = imageSize.height(); |
- |
- int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight)); |
- int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight)); |
- int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth)); |
- int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth)); |
- |
- ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); |
- ENinePieceImageRule vRule = ninePieceImage.verticalRule(); |
- |
- bool fitToBorder = style->borderImage() == ninePieceImage; |
- |
- int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; |
- int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; |
- int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; |
- int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; |
- |
- bool drawLeft = leftSlice > 0 && leftWidth > 0; |
- bool drawTop = topSlice > 0 && topWidth > 0; |
- bool drawRight = rightSlice > 0 && rightWidth > 0; |
- bool drawBottom = bottomSlice > 0 && bottomWidth > 0; |
- bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && |
- (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; |
- |
- Image* image = styleImage->image(this, imageSize); |
- |
- if (drawLeft) { |
- // Paint the top and bottom left corners. |
- |
- // The top left corner rect is (tx, ty, leftWidth, topWidth) |
- // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) |
- if (drawTop) |
- graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth), |
- IntRect(0, 0, leftSlice, topSlice), op); |
- |
- // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) |
- // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) |
- if (drawBottom) |
- graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), |
- IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); |
- |
- // Paint the left edge. |
- // Have to scale and tile into the border rect. |
- graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth, |
- h - topWidth - bottomWidth), |
- IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), |
- Image::StretchTile, (Image::TileRule)vRule, op); |
- } |
- |
- if (drawRight) { |
- // Paint the top and bottom right corners |
- // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) |
- // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) |
- if (drawTop) |
- graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), |
- IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); |
- |
- // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) |
- // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) |
- if (drawBottom) |
- graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), |
- IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); |
- |
- // Paint the right edge. |
- graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, |
- h - topWidth - bottomWidth), |
- IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), |
- Image::StretchTile, (Image::TileRule)vRule, op); |
- } |
- |
- // Paint the top edge. |
- if (drawTop) |
- graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), |
- IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), |
- (Image::TileRule)hRule, Image::StretchTile, op); |
- |
- // Paint the bottom edge. |
- if (drawBottom) |
- graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth, |
- w - leftWidth - rightWidth, bottomWidth), |
- IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), |
- (Image::TileRule)hRule, Image::StretchTile, op); |
- |
- // Paint the middle. |
- if (drawMiddle) |
- graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, |
- h - topWidth - bottomWidth), |
- IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), |
- (Image::TileRule)hRule, (Image::TileRule)vRule, op); |
- |
- // Clear the clip for the border radius. |
- if (clipped) |
- graphicsContext->restore(); |
- |
- return true; |
-} |
- |
-void RenderObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, |
- const RenderStyle* style, bool begin, bool end) |
-{ |
- if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) |
- return; |
- |
- const Color& tc = style->borderTopColor(); |
- const Color& bc = style->borderBottomColor(); |
- const Color& lc = style->borderLeftColor(); |
- const Color& rc = style->borderRightColor(); |
- |
- bool tt = style->borderTopIsTransparent(); |
- bool bt = style->borderBottomIsTransparent(); |
- bool rt = style->borderRightIsTransparent(); |
- bool lt = style->borderLeftIsTransparent(); |
- |
- EBorderStyle ts = style->borderTopStyle(); |
- EBorderStyle bs = style->borderBottomStyle(); |
- EBorderStyle ls = style->borderLeftStyle(); |
- EBorderStyle rs = style->borderRightStyle(); |
- |
- bool renderTop = ts > BHIDDEN && !tt; |
- bool renderLeft = ls > BHIDDEN && begin && !lt; |
- bool renderRight = rs > BHIDDEN && end && !rt; |
- bool renderBottom = bs > BHIDDEN && !bt; |
- |
- // Need sufficient width and height to contain border radius curves. Sanity check our border radii |
- // and our width/height values to make sure the curves can all fit. If not, then we won't paint |
- // any border radii. |
- bool renderRadii = false; |
- IntSize topLeft = style->borderTopLeftRadius(); |
- IntSize topRight = style->borderTopRightRadius(); |
- IntSize bottomLeft = style->borderBottomLeftRadius(); |
- IntSize bottomRight = style->borderBottomRightRadius(); |
- |
- if (style->hasBorderRadius() && |
- static_cast<unsigned>(w) >= static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()) && |
- static_cast<unsigned>(w) >= static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width()) && |
- static_cast<unsigned>(h) >= static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height()) && |
- static_cast<unsigned>(h) >= static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height())) |
- renderRadii = true; |
- |
- // Clip to the rounded rectangle. |
- if (renderRadii) { |
- graphicsContext->save(); |
- graphicsContext->addRoundedRectClip(IntRect(tx, ty, w, h), topLeft, topRight, bottomLeft, bottomRight); |
- } |
- |
- int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; |
- float thickness; |
- bool upperLeftBorderStylesMatch = renderLeft && (ts == ls) && (tc == lc); |
- bool upperRightBorderStylesMatch = renderRight && (ts == rs) && (tc == rc) && (ts != OUTSET) && (ts != RIDGE) && (ts != INSET) && (ts != GROOVE); |
- bool lowerLeftBorderStylesMatch = renderLeft && (bs == ls) && (bc == lc) && (bs != OUTSET) && (bs != RIDGE) && (bs != INSET) && (bs != GROOVE); |
- bool lowerRightBorderStylesMatch = renderRight && (bs == rs) && (bc == rc); |
- |
- if (renderTop) { |
- bool ignore_left = (renderRadii && topLeft.width() > 0) || |
- (tc == lc && tt == lt && ts >= OUTSET && |
- (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); |
- |
- bool ignore_right = (renderRadii && topRight.width() > 0) || |
- (tc == rc && tt == rt && ts >= OUTSET && |
- (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); |
- |
- int x = tx; |
- int x2 = tx + w; |
- if (renderRadii) { |
- x += topLeft.width(); |
- x2 -= topRight.width(); |
- } |
- |
- drawBorder(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, |
- ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); |
- |
- if (renderRadii) { |
- int leftY = ty; |
- |
- // We make the arc double thick and let the clip rect take care of clipping the extra off. |
- // We're doing this because it doesn't seem possible to match the curve of the clip exactly |
- // with the arc-drawing function. |
- thickness = style->borderTopWidth() * 2; |
- |
- if (topLeft.width()) { |
- int leftX = tx; |
- // The inner clip clips inside the arc. This is especially important for 1px borders. |
- bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) |
- && (style->borderTopWidth() < topLeft.height()) |
- && (ts != DOUBLE || style->borderTopWidth() > 6); |
- if (applyLeftInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), |
- style->borderTopWidth()); |
- } |
- |
- firstAngleStart = 90; |
- firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; |
- |
- // Draw upper left arc |
- drawBorderArc(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, |
- BSTop, tc, style->color(), ts, true); |
- if (applyLeftInnerClip) |
- graphicsContext->restore(); |
- } |
- |
- if (topRight.width()) { |
- int rightX = tx + w - topRight.width() * 2; |
- bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) |
- && (style->borderTopWidth() < topRight.height()) |
- && (ts != DOUBLE || style->borderTopWidth() > 6); |
- if (applyRightInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), |
- style->borderTopWidth()); |
- } |
- |
- if (upperRightBorderStylesMatch) { |
- secondAngleStart = 0; |
- secondAngleSpan = 90; |
- } else { |
- secondAngleStart = 45; |
- secondAngleSpan = 45; |
- } |
- |
- // Draw upper right arc |
- drawBorderArc(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, |
- BSTop, tc, style->color(), ts, false); |
- if (applyRightInnerClip) |
- graphicsContext->restore(); |
- } |
- } |
- } |
- |
- if (renderBottom) { |
- bool ignore_left = (renderRadii && bottomLeft.width() > 0) || |
- (bc == lc && bt == lt && bs >= OUTSET && |
- (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); |
- |
- bool ignore_right = (renderRadii && bottomRight.width() > 0) || |
- (bc == rc && bt == rt && bs >= OUTSET && |
- (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); |
- |
- int x = tx; |
- int x2 = tx + w; |
- if (renderRadii) { |
- x += bottomLeft.width(); |
- x2 -= bottomRight.width(); |
- } |
- |
- drawBorder(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bc, style->color(), bs, |
- ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); |
- |
- if (renderRadii) { |
- thickness = style->borderBottomWidth() * 2; |
- |
- if (bottomLeft.width()) { |
- int leftX = tx; |
- int leftY = ty + h - bottomLeft.height() * 2; |
- bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) |
- && (style->borderBottomWidth() < bottomLeft.height()) |
- && (bs != DOUBLE || style->borderBottomWidth() > 6); |
- if (applyLeftInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), |
- style->borderBottomWidth()); |
- } |
- |
- if (lowerLeftBorderStylesMatch) { |
- firstAngleStart = 180; |
- firstAngleSpan = 90; |
- } else { |
- firstAngleStart = 225; |
- firstAngleSpan = 45; |
- } |
- |
- // Draw lower left arc |
- drawBorderArc(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, |
- BSBottom, bc, style->color(), bs, true); |
- if (applyLeftInnerClip) |
- graphicsContext->restore(); |
- } |
- |
- if (bottomRight.width()) { |
- int rightY = ty + h - bottomRight.height() * 2; |
- int rightX = tx + w - bottomRight.width() * 2; |
- bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) |
- && (style->borderBottomWidth() < bottomRight.height()) |
- && (bs != DOUBLE || style->borderBottomWidth() > 6); |
- if (applyRightInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), |
- style->borderBottomWidth()); |
- } |
- |
- secondAngleStart = 270; |
- secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; |
- |
- // Draw lower right arc |
- drawBorderArc(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, |
- BSBottom, bc, style->color(), bs, false); |
- if (applyRightInnerClip) |
- graphicsContext->restore(); |
- } |
- } |
- } |
- |
- if (renderLeft) { |
- bool ignore_top = (renderRadii && topLeft.height() > 0) || |
- (tc == lc && tt == lt && ls >= OUTSET && |
- (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); |
- |
- bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || |
- (bc == lc && bt == lt && ls >= OUTSET && |
- (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); |
- |
- int y = ty; |
- int y2 = ty + h; |
- if (renderRadii) { |
- y += topLeft.height(); |
- y2 -= bottomLeft.height(); |
- } |
- |
- drawBorder(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, |
- ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); |
- |
- if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { |
- int topX = tx; |
- thickness = style->borderLeftWidth() * 2; |
- |
- if (!upperLeftBorderStylesMatch && topLeft.width()) { |
- int topY = ty; |
- bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) |
- && (style->borderTopWidth() < topLeft.height()) |
- && (ls != DOUBLE || style->borderLeftWidth() > 6); |
- if (applyTopInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), |
- style->borderLeftWidth()); |
- } |
- |
- firstAngleStart = 135; |
- firstAngleSpan = 45; |
- |
- // Draw top left arc |
- drawBorderArc(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, |
- BSLeft, lc, style->color(), ls, true); |
- if (applyTopInnerClip) |
- graphicsContext->restore(); |
- } |
- |
- if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { |
- int bottomY = ty + h - bottomLeft.height() * 2; |
- bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) |
- && (style->borderBottomWidth() < bottomLeft.height()) |
- && (ls != DOUBLE || style->borderLeftWidth() > 6); |
- if (applyBottomInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), |
- style->borderLeftWidth()); |
- } |
- |
- secondAngleStart = 180; |
- secondAngleSpan = 45; |
- |
- // Draw bottom left arc |
- drawBorderArc(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, |
- BSLeft, lc, style->color(), ls, false); |
- if (applyBottomInnerClip) |
- graphicsContext->restore(); |
- } |
- } |
- } |
- |
- if (renderRight) { |
- bool ignore_top = (renderRadii && topRight.height() > 0) || |
- ((tc == rc) && (tt == rt) && |
- (rs >= DOTTED || rs == INSET) && |
- (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); |
- |
- bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || |
- ((bc == rc) && (bt == rt) && |
- (rs >= DOTTED || rs == INSET) && |
- (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); |
- |
- int y = ty; |
- int y2 = ty + h; |
- if (renderRadii) { |
- y += topRight.height(); |
- y2 -= bottomRight.height(); |
- } |
- |
- drawBorder(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rc, style->color(), rs, |
- ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); |
- |
- if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { |
- thickness = style->borderRightWidth() * 2; |
- |
- if (!upperRightBorderStylesMatch && topRight.width()) { |
- int topX = tx + w - topRight.width() * 2; |
- int topY = ty; |
- bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) |
- && (style->borderTopWidth() < topRight.height()) |
- && (rs != DOUBLE || style->borderRightWidth() > 6); |
- if (applyTopInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), |
- style->borderRightWidth()); |
- } |
- |
- firstAngleStart = 0; |
- firstAngleSpan = 45; |
- |
- // Draw top right arc |
- drawBorderArc(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, |
- BSRight, rc, style->color(), rs, true); |
- if (applyTopInnerClip) |
- graphicsContext->restore(); |
- } |
- |
- if (!lowerRightBorderStylesMatch && bottomRight.width()) { |
- int bottomX = tx + w - bottomRight.width() * 2; |
- int bottomY = ty + h - bottomRight.height() * 2; |
- bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) |
- && (style->borderBottomWidth() < bottomRight.height()) |
- && (rs != DOUBLE || style->borderRightWidth() > 6); |
- if (applyBottomInnerClip) { |
- graphicsContext->save(); |
- graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), |
- style->borderRightWidth()); |
- } |
- |
- secondAngleStart = 315; |
- secondAngleSpan = 45; |
- |
- // Draw bottom right arc |
- drawBorderArc(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, |
- BSRight, rc, style->color(), rs, false); |
- if (applyBottomInnerClip) |
- graphicsContext->restore(); |
- } |
- } |
- } |
- |
- if (renderRadii) |
- graphicsContext->restore(); |
-} |
- |
-void RenderObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, bool begin, bool end) |
-{ |
- // FIXME: Deal with border-image. Would be great to use border-image as a mask. |
- |
- IntRect rect(tx, ty, w, h); |
- bool hasBorderRadius = s->hasBorderRadius(); |
- bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255; |
- for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) { |
- context->save(); |
- |
- IntSize shadowOffset(shadow->x, shadow->y); |
- int shadowBlur = shadow->blur; |
- IntRect fillRect(rect); |
- |
- if (hasBorderRadius) { |
- IntRect shadowRect(rect); |
- shadowRect.inflate(shadowBlur); |
- shadowRect.move(shadowOffset); |
- context->clip(shadowRect); |
- |
- // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not |
- // bleed in (due to antialiasing) if the context is transformed. |
- IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 1, 0); |
- shadowOffset -= extraOffset; |
- fillRect.move(extraOffset); |
- } |
- |
- context->setShadow(shadowOffset, shadowBlur, shadow->color); |
- if (hasBorderRadius) { |
- IntSize topLeft = begin ? s->borderTopLeftRadius() : IntSize(); |
- IntSize topRight = end ? s->borderTopRightRadius() : IntSize(); |
- IntSize bottomLeft = begin ? s->borderBottomLeftRadius() : IntSize(); |
- IntSize bottomRight = end ? s->borderBottomRightRadius() : IntSize(); |
- if (!hasOpaqueBackground) |
- context->clipOutRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); |
- context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); |
- } else { |
- if (!hasOpaqueBackground) |
- context->clipOut(rect); |
- context->fillRect(fillRect, Color::black); |
- } |
- context->restore(); |
- } |
-} |
- |
-void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect) |
-{ |
- if (rect.isEmpty()) |
- return; |
- Node* node = element(); |
- if (!node || !node->isLink() || !node->isElementNode()) |
- return; |
- const AtomicString& href = static_cast<Element*>(node)->getAttribute(hrefAttr); |
- if (href.isNull()) |
- return; |
- context->setURLForRect(node->document()->completeURL(href), rect); |
-} |
- |
-void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style) |
-{ |
- if (!hasOutline()) |
- return; |
- |
- int ow = style->outlineWidth(); |
- EBorderStyle os = style->outlineStyle(); |
- |
- Color oc = style->outlineColor(); |
- if (!oc.isValid()) |
- oc = style->color(); |
- |
- int offset = style->outlineOffset(); |
- |
- if (style->outlineStyleIsAuto() || hasOutlineAnnotation()) { |
- if (!theme()->supportsFocusRing(style)) { |
- // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. |
- graphicsContext->initFocusRing(ow, offset); |
- addFocusRingRects(graphicsContext, tx, ty); |
- if (style->outlineStyleIsAuto()) |
- graphicsContext->drawFocusRing(oc); |
- else |
- addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); |
- graphicsContext->clearFocusRing(); |
- } |
- } |
- |
- if (style->outlineStyleIsAuto() || style->outlineStyle() == BNONE) |
- return; |
- |
- tx -= offset; |
- ty -= offset; |
- w += 2 * offset; |
- h += 2 * offset; |
- |
- if (h < 0 || w < 0) |
- return; |
- |
- drawBorder(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, |
- BSLeft, Color(oc), style->color(), os, ow, ow); |
- |
- drawBorder(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, |
- BSTop, Color(oc), style->color(), os, ow, ow); |
- |
- drawBorder(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, |
- BSRight, Color(oc), style->color(), os, ow, ow); |
- |
- drawBorder(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, |
- BSBottom, Color(oc), style->color(), os, ow, ow); |
-} |
- |
- |
-void RenderObject::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool) |
-{ |
- if (!virtualChildren()->firstChild()) { |
- if ((isInline() || isAnonymousBlock())) { |
- FloatPoint absPos = localToAbsolute(FloatPoint()); |
- absoluteRects(rects, absPos.x(), absPos.y()); |
- } |
- return; |
- } |
- |
- unsigned offset = start; |
- for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { |
- if (child->isText() || child->isInline() || child->isAnonymousBlock()) { |
- FloatPoint absPos = child->localToAbsolute(FloatPoint()); |
- child->absoluteRects(rects, absPos.x(), absPos.y()); |
- } |
- } |
-} |
- |
-void RenderObject::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool) |
-{ |
- if (!virtualChildren()->firstChild()) { |
- if (isInline() || isAnonymousBlock()) |
- absoluteQuads(quads); |
- return; |
- } |
- |
- unsigned offset = start; |
- for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { |
- if (child->isText() || child->isInline() || child->isAnonymousBlock()) |
- child->absoluteQuads(quads); |
- } |
-} |
- |
-IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) |
-{ |
- if (useTransforms) { |
- Vector<FloatQuad> quads; |
- absoluteQuads(quads); |
- |
- size_t n = quads.size(); |
- if (!n) |
- return IntRect(); |
- |
- IntRect result = quads[0].enclosingBoundingBox(); |
- for (size_t i = 1; i < n; ++i) |
- result.unite(quads[i].enclosingBoundingBox()); |
- return result; |
- } |
- |
- FloatPoint absPos = localToAbsolute(); |
- Vector<IntRect> rects; |
- absoluteRects(rects, absPos.x(), absPos.y()); |
- |
- size_t n = rects.size(); |
- if (!n) |
- return IntRect(); |
- |
- IntRect result = rects[0]; |
- for (size_t i = 1; i < n; ++i) |
- result.unite(rects[i]); |
- return result; |
-} |
- |
-void RenderObject::addAbsoluteRectForLayer(IntRect& result) |
-{ |
- if (hasLayer()) |
- result.unite(absoluteBoundingBoxRect()); |
- for (RenderObject* current = firstChild(); current; current = current->nextSibling()) |
- current->addAbsoluteRectForLayer(result); |
-} |
- |
-IntRect RenderObject::paintingRootRect(IntRect& topLevelRect) |
-{ |
- IntRect result = absoluteBoundingBoxRect(); |
- topLevelRect = result; |
- for (RenderObject* current = firstChild(); current; current = current->nextSibling()) |
- current->addAbsoluteRectForLayer(result); |
- return result; |
-} |
- |
-void RenderObject::paint(PaintInfo& /*paintInfo*/, int /*tx*/, int /*ty*/) |
-{ |
-} |
- |
-RenderBoxModelObject* RenderObject::containerForRepaint() const |
-{ |
-#if USE(ACCELERATED_COMPOSITING) |
- if (RenderView* v = view()) { |
- if (v->usesCompositing()) { |
- RenderLayer* compLayer = enclosingCompositingLayer(); |
- return compLayer ? compLayer->renderer() : 0; |
- } |
- } |
-#endif |
- // Do root-relative repaint. |
- return 0; |
-} |
- |
-void RenderObject::repaintUsingContainer(RenderBoxModelObject* repaintContainer, const IntRect& r, bool immediate) |
-{ |
- if (!repaintContainer || repaintContainer->isRenderView()) { |
- RenderView* v = repaintContainer ? toRenderView(repaintContainer) : view(); |
- v->repaintViewRectangle(r, immediate); |
- } else { |
-#if USE(ACCELERATED_COMPOSITING) |
- RenderView* v = view(); |
- if (v->usesCompositing()) { |
- ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); |
- repaintContainer->layer()->setBackingNeedsRepaintInRect(r); |
- } |
-#else |
- ASSERT_NOT_REACHED(); |
-#endif |
- } |
-} |
- |
-void RenderObject::repaint(bool immediate) |
-{ |
- // Can't use view(), since we might be unrooted. |
- RenderObject* o = this; |
- while (o->parent()) |
- o = o->parent(); |
- if (!o->isRenderView()) |
- return; |
- |
- RenderView* view = toRenderView(o); |
- if (view->printing()) |
- return; // Don't repaint if we're printing. |
- |
- RenderBoxModelObject* repaintContainer = containerForRepaint(); |
- repaintUsingContainer(repaintContainer ? repaintContainer : view, clippedOverflowRectForRepaint(repaintContainer), immediate); |
-} |
- |
-void RenderObject::repaintRectangle(const IntRect& r, bool immediate) |
-{ |
- // Can't use view(), since we might be unrooted. |
- RenderObject* o = this; |
- while (o->parent()) |
- o = o->parent(); |
- if (!o->isRenderView()) |
- return; |
- |
- RenderView* view = toRenderView(o); |
- if (view->printing()) |
- return; // Don't repaint if we're printing. |
- |
- IntRect dirtyRect(r); |
- |
- // FIXME: layoutDelta needs to be applied in parts before/after transforms and |
- // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 |
- dirtyRect.move(view->layoutDelta()); |
- |
- RenderBoxModelObject* repaintContainer = containerForRepaint(); |
- computeRectForRepaint(repaintContainer, dirtyRect); |
- repaintUsingContainer(repaintContainer ? repaintContainer : view, dirtyRect, immediate); |
-} |
- |
-bool RenderObject::repaintAfterLayoutIfNeeded(RenderBoxModelObject* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox) |
-{ |
- RenderView* v = view(); |
- if (v->printing()) |
- return false; // Don't repaint if we're printing. |
- |
- IntRect newBounds = clippedOverflowRectForRepaint(repaintContainer); |
- IntRect newOutlineBox; |
- |
- bool fullRepaint = selfNeedsLayout(); |
- // Presumably a background or a border exists if border-fit:lines was specified. |
- if (!fullRepaint && style()->borderFit() == BorderFitLines) |
- fullRepaint = true; |
- if (!fullRepaint) { |
- newOutlineBox = outlineBoundsForRepaint(repaintContainer); |
- if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) |
- fullRepaint = true; |
- } |
- |
- if (!repaintContainer) |
- repaintContainer = v; |
- |
- if (fullRepaint) { |
- repaintUsingContainer(repaintContainer, oldBounds); |
- if (newBounds != oldBounds) |
- repaintUsingContainer(repaintContainer, newBounds); |
- return true; |
- } |
- |
- if (newBounds == oldBounds && newOutlineBox == oldOutlineBox) |
- return false; |
- |
- int deltaLeft = newBounds.x() - oldBounds.x(); |
- if (deltaLeft > 0) |
- repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); |
- else if (deltaLeft < 0) |
- repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); |
- |
- int deltaRight = newBounds.right() - oldBounds.right(); |
- if (deltaRight > 0) |
- repaintUsingContainer(repaintContainer, IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height())); |
- else if (deltaRight < 0) |
- repaintUsingContainer(repaintContainer, IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height())); |
- |
- int deltaTop = newBounds.y() - oldBounds.y(); |
- if (deltaTop > 0) |
- repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); |
- else if (deltaTop < 0) |
- repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); |
- |
- int deltaBottom = newBounds.bottom() - oldBounds.bottom(); |
- if (deltaBottom > 0) |
- repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom)); |
- else if (deltaBottom < 0) |
- repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom)); |
- |
- if (newOutlineBox == oldOutlineBox) |
- return false; |
- |
- // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly |
- // two rectangles (but typically only one). |
- RenderStyle* outlineStyle = outlineStyleForRepaint(); |
- int ow = outlineStyle->outlineSize(); |
- ShadowData* boxShadow = style()->boxShadow(); |
- int width = abs(newOutlineBox.width() - oldOutlineBox.width()); |
- if (width) { |
- int shadowRight = 0; |
- for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) |
- shadowRight = max(shadow->x + shadow->blur, shadowRight); |
- |
- int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0; |
- int borderWidth = max(-outlineStyle->outlineOffset(), max(borderRight, max(style()->borderTopRightRadius().width(), style()->borderBottomRightRadius().width()))) + max(ow, shadowRight); |
- IntRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - borderWidth, |
- newOutlineBox.y(), |
- width + borderWidth, |
- max(newOutlineBox.height(), oldOutlineBox.height())); |
- int right = min(newBounds.right(), oldBounds.right()); |
- if (rightRect.x() < right) { |
- rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); |
- repaintUsingContainer(repaintContainer, rightRect); |
- } |
- } |
- int height = abs(newOutlineBox.height() - oldOutlineBox.height()); |
- if (height) { |
- int shadowBottom = 0; |
- for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) |
- shadowBottom = max(shadow->y + shadow->blur, shadowBottom); |
- |
- int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0; |
- int borderHeight = max(-outlineStyle->outlineOffset(), max(borderBottom, max(style()->borderBottomLeftRadius().height(), style()->borderBottomRightRadius().height()))) + max(ow, shadowBottom); |
- IntRect bottomRect(newOutlineBox.x(), |
- min(newOutlineBox.bottom(), oldOutlineBox.bottom()) - borderHeight, |
- max(newOutlineBox.width(), oldOutlineBox.width()), |
- height + borderHeight); |
- int bottom = min(newBounds.bottom(), oldBounds.bottom()); |
- if (bottomRect.y() < bottom) { |
- bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); |
- repaintUsingContainer(repaintContainer, bottomRect); |
- } |
- } |
- return false; |
-} |
- |
-void RenderObject::repaintDuringLayoutIfMoved(const IntRect&) |
-{ |
-} |
- |
-void RenderObject::repaintOverhangingFloats(bool) |
-{ |
-} |
- |
-bool RenderObject::checkForRepaintDuringLayout() const |
-{ |
- // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=20885> It is probably safe to also require |
- // m_everHadLayout. Currently, only RenderBlock::layoutBlock() adds this condition. See also |
- // <https://bugs.webkit.org/show_bug.cgi?id=15129>. |
- return !document()->view()->needsFullRepaint() && !hasLayer(); |
-} |
- |
-IntRect RenderObject::rectWithOutlineForRepaint(RenderBoxModelObject* repaintContainer, int outlineWidth) |
-{ |
- IntRect r(clippedOverflowRectForRepaint(repaintContainer)); |
- r.inflate(outlineWidth); |
- return r; |
-} |
- |
-IntRect RenderObject::clippedOverflowRectForRepaint(RenderBoxModelObject*) |
-{ |
- ASSERT_NOT_REACHED(); |
- return IntRect(); |
-} |
- |
-void RenderObject::computeRectForRepaint(RenderBoxModelObject* repaintContainer, IntRect& rect, bool fixed) |
-{ |
- if (repaintContainer == this) |
- return; |
- |
- if (RenderObject* o = parent()) { |
- if (o->isBlockFlow()) { |
- RenderBlock* cb = toRenderBlock(o); |
- if (cb->hasColumns()) |
- cb->adjustRectForColumns(rect); |
- } |
- |
- if (o->hasOverflowClip()) { |
- // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the |
- // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint |
- // anyway if its size does change. |
- RenderBox* boxParent = toRenderBox(o); |
- |
- IntRect boxRect(0, 0, boxParent->layer()->width(), boxParent->layer()->height()); |
- int x = rect.x(); |
- int y = rect.y(); |
- boxParent->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden. |
- IntRect repaintRect(x, y, rect.width(), rect.height()); |
- rect = intersection(repaintRect, boxRect); |
- if (rect.isEmpty()) |
- return; |
- } |
- |
- o->computeRectForRepaint(repaintContainer, rect, fixed); |
- } |
-} |
- |
-void RenderObject::dirtyLinesFromChangedChild(RenderObject*) |
-{ |
-} |
- |
-#ifndef NDEBUG |
- |
-void RenderObject::showTreeForThis() const |
-{ |
- if (element()) |
- element()->showTreeForThis(); |
-} |
- |
-#endif // NDEBUG |
- |
-Color RenderObject::selectionBackgroundColor() const |
-{ |
- Color color; |
- if (style()->userSelect() != SELECT_NONE) { |
- RenderStyle* pseudoStyle = getCachedPseudoStyle(SELECTION); |
- if (pseudoStyle && pseudoStyle->backgroundColor().isValid()) |
- color = pseudoStyle->backgroundColor().blendWithWhite(); |
- else |
- color = document()->frame()->selection()->isFocusedAndActive() ? |
- theme()->activeSelectionBackgroundColor() : |
- theme()->inactiveSelectionBackgroundColor(); |
- } |
- |
- return color; |
-} |
- |
-Color RenderObject::selectionForegroundColor() const |
-{ |
- Color color; |
- if (style()->userSelect() == SELECT_NONE) |
- return color; |
- |
- if (RenderStyle* pseudoStyle = getCachedPseudoStyle(SELECTION)) { |
- color = pseudoStyle->textFillColor(); |
- if (!color.isValid()) |
- color = pseudoStyle->color(); |
- } else |
- color = document()->frame()->selection()->isFocusedAndActive() ? |
- theme()->activeSelectionForegroundColor() : |
- theme()->inactiveSelectionForegroundColor(); |
- |
- return color; |
-} |
- |
-Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const |
-{ |
- if (!dhtmlOK && !uaOK) |
- return 0; |
- |
- for (const RenderObject* curr = this; curr; curr = curr->parent()) { |
- Node* elt = curr->element(); |
- if (elt && elt->nodeType() == Node::TEXT_NODE) { |
- // Since there's no way for the author to address the -webkit-user-drag style for a text node, |
- // we use our own judgement. |
- if (uaOK && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { |
- dhtmlWillDrag = false; |
- return curr->node(); |
- } |
- if (elt->canStartSelection()) |
- // In this case we have a click in the unselected portion of text. If this text is |
- // selectable, we want to start the selection process instead of looking for a parent |
- // to try to drag. |
- return 0; |
- } else { |
- EUserDrag dragMode = curr->style()->userDrag(); |
- if (dhtmlOK && dragMode == DRAG_ELEMENT) { |
- dhtmlWillDrag = true; |
- return curr->node(); |
- } |
- if (uaOK && dragMode == DRAG_AUTO |
- && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { |
- dhtmlWillDrag = false; |
- return curr->node(); |
- } |
- } |
- } |
- return 0; |
-} |
- |
-void RenderObject::selectionStartEnd(int& spos, int& epos) const |
-{ |
- view()->selectionStartEnd(spos, epos); |
-} |
- |
-RenderBlock* RenderObject::createAnonymousBlock() |
-{ |
- RefPtr<RenderStyle> newStyle = RenderStyle::create(); |
- newStyle->inheritFrom(m_style.get()); |
- newStyle->setDisplay(BLOCK); |
- |
- RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); |
- newBox->setStyle(newStyle.release()); |
- return newBox; |
-} |
- |
-void RenderObject::handleDynamicFloatPositionChange() |
-{ |
- // We have gone from not affecting the inline status of the parent flow to suddenly |
- // having an impact. See if there is a mismatch between the parent flow's |
- // childrenInline() state and our state. |
- setInline(style()->isDisplayInlineType()); |
- if (isInline() != parent()->childrenInline()) { |
- if (!isInline()) |
- toRenderBox(parent())->childBecameNonInline(this); |
- else { |
- // An anonymous block must be made to wrap this inline. |
- RenderBlock* block = createAnonymousBlock(); |
- RenderObjectChildList* childlist = parent()->virtualChildren(); |
- childlist->insertChildNode(parent(), block, this); |
- block->children()->appendChildNode(block, childlist->removeChildNode(parent(), this)); |
- } |
- } |
-} |
- |
-void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) |
-{ |
- if (!isText() && style) |
- setStyle(animation()->updateAnimations(this, style.get())); |
- else |
- setStyle(style); |
-} |
- |
-void RenderObject::setStyle(PassRefPtr<RenderStyle> style) |
-{ |
- if (m_style == style) |
- return; |
- |
- StyleDifference diff = StyleDifferenceEqual; |
- if (m_style) |
- diff = m_style->diff(style.get()); |
- |
- // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. |
- if (diff == StyleDifferenceRepaintLayer && !hasLayer()) |
- diff = StyleDifferenceRepaint; |
- |
- styleWillChange(diff, style.get()); |
- |
- RefPtr<RenderStyle> oldStyle = m_style.release(); |
- m_style = style; |
- |
- updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0); |
- updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0); |
- |
- updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); |
- updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); |
- |
- // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen |
- // during styleDidChange (it's used by clippedOverflowRectForRepaint()). |
- if (m_style->outlineWidth() > 0 && m_style->outlineSize() > maximalOutlineSize(PaintPhaseOutline)) |
- toRenderView(document()->renderer())->setMaximalOutlineSize(m_style->outlineSize()); |
- |
- styleDidChange(diff, oldStyle.get()); |
-} |
- |
-void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style) |
-{ |
- m_style = style; |
-} |
- |
-void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) |
-{ |
- if (m_style) { |
- // If our z-index changes value or our visibility changes, |
- // we need to dirty our stacking context's z-order list. |
- if (newStyle) { |
-#if ENABLE(DASHBOARD_SUPPORT) |
- if (m_style->visibility() != newStyle->visibility() || |
- m_style->zIndex() != newStyle->zIndex() || |
- m_style->hasAutoZIndex() != newStyle->hasAutoZIndex()) |
- document()->setDashboardRegionsDirty(true); |
-#endif |
- |
- // Keep layer hierarchy visibility bits up to date if visibility changes. |
- if (m_style->visibility() != newStyle->visibility()) { |
- if (RenderLayer* l = enclosingLayer()) { |
- if (newStyle->visibility() == VISIBLE) |
- l->setHasVisibleContent(true); |
- else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { |
- l->dirtyVisibleContentStatus(); |
- if (diff > StyleDifferenceRepaintLayer) |
- repaint(); |
- } |
- } |
- } |
- } |
- |
- if (m_parent && (diff == StyleDifferenceRepaint || newStyle->outlineSize() < m_style->outlineSize())) |
- repaint(); |
- if (isFloating() && (m_style->floating() != newStyle->floating())) |
- // For changes in float styles, we need to conceivably remove ourselves |
- // from the floating objects list. |
- toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); |
- else if (isPositioned() && (newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)) |
- // For changes in positioning styles, we need to conceivably remove ourselves |
- // from the positioned objects list. |
- toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); |
- |
- s_affectsParentBlock = isFloatingOrPositioned() && |
- (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition) |
- && parent() && (parent()->isBlockFlow() || parent()->isRenderInline()); |
- |
- // reset style flags |
- if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { |
- m_floating = false; |
- m_positioned = false; |
- m_relPositioned = false; |
- } |
- m_paintBackground = false; |
- m_hasOverflowClip = false; |
- m_hasTransform = false; |
- m_hasReflection = false; |
- } else |
- s_affectsParentBlock = false; |
- |
- if (view()->frameView()) { |
- // FIXME: A better solution would be to only invalidate the fixed regions when scrolling. It's overkill to |
- // prevent the entire view from blitting on a scroll. |
- bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition || newStyle->hasFixedBackgroundImage()); |
- bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition || m_style->hasFixedBackgroundImage()); |
- if (oldStyleSlowScroll != newStyleSlowScroll) { |
- if (oldStyleSlowScroll) |
- view()->frameView()->removeSlowRepaintObject(); |
- if (newStyleSlowScroll) |
- view()->frameView()->addSlowRepaintObject(); |
- } |
- } |
-} |
- |
-void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) |
-{ |
- if (s_affectsParentBlock) |
- handleDynamicFloatPositionChange(); |
- |
- if (!m_parent) |
- return; |
- |
- if (diff == StyleDifferenceLayout) |
- setNeedsLayoutAndPrefWidthsRecalc(); |
- else if (diff == StyleDifferenceLayoutPositionedMovementOnly) |
- setNeedsPositionedMovementLayout(); |
- else if (diff == StyleDifferenceRepaintLayer || diff == StyleDifferenceRepaint) |
- // Do a repaint with the new style now, e.g., for example if we go from |
- // not having an outline to having an outline. |
- repaint(); |
-} |
- |
-void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) |
-{ |
- // FIXME: This will be slow when a large number of images is used. Fix by using a dict. |
- for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { |
- if (currOld->image() && (!newLayers || !newLayers->containsImage(currOld->image()))) |
- currOld->image()->removeClient(this); |
- } |
- for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { |
- if (currNew->image() && (!oldLayers || !oldLayers->containsImage(currNew->image()))) |
- currNew->image()->addClient(this); |
- } |
-} |
- |
-void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) |
-{ |
- if (oldImage != newImage) { |
- if (oldImage) |
- oldImage->removeClient(this); |
- if (newImage) |
- newImage->addClient(this); |
- } |
-} |
- |
-IntRect RenderObject::viewRect() const |
-{ |
- return view()->viewRect(); |
-} |
- |
-FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const |
-{ |
- RenderObject* o = parent(); |
- if (o) { |
- if (o->hasOverflowClip()) |
- localPoint -= toRenderBox(o)->layer()->scrolledContentOffset(); |
- return o->localToAbsolute(localPoint, fixed, useTransforms); |
- } |
- |
- return FloatPoint(); |
-} |
- |
-FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const |
-{ |
- RenderObject* o = parent(); |
- if (o) { |
- FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); |
- if (o->hasOverflowClip()) |
- localPoint += toRenderBox(o)->layer()->scrolledContentOffset(); |
- return localPoint; |
- } |
- return FloatPoint(); |
-} |
- |
-FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, RenderBoxModelObject* repaintContainer, bool fixed) const |
-{ |
- if (repaintContainer == this) |
- return localQuad; |
- |
- RenderObject* o = parent(); |
- if (o) { |
- FloatQuad quad = localQuad; |
- if (o->hasOverflowClip()) |
- quad -= toRenderBox(o)->layer()->scrolledContentOffset(); |
- return o->localToContainerQuad(quad, repaintContainer, fixed); |
- } |
- |
- return FloatQuad(); |
-} |
- |
-IntSize RenderObject::offsetFromContainer(RenderObject* o) const |
-{ |
- ASSERT(o == container()); |
- |
- IntSize offset; |
- if (o->hasOverflowClip()) |
- offset -= toRenderBox(o)->layer()->scrolledContentOffset(); |
- |
- return offset; |
-} |
- |
-IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine) |
-{ |
- if (extraWidthToEndOfLine) |
- *extraWidthToEndOfLine = 0; |
- |
- return IntRect(); |
-} |
- |
-RenderView* RenderObject::view() const |
-{ |
- return toRenderView(document()->renderer()); |
-} |
- |
-bool RenderObject::hasOutlineAnnotation() const |
-{ |
- return element() && element()->isLink() && document()->printing(); |
-} |
- |
-RenderObject* RenderObject::container() const |
-{ |
- // This method is extremely similar to containingBlock(), but with a few notable |
- // exceptions. |
- // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when |
- // the object is not part of the primary document subtree yet. |
- // (2) For normal flow elements, it just returns the parent. |
- // (3) For absolute positioned elements, it will return a relative positioned inline. |
- // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle |
- // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and |
- // calcAbsoluteVertical have to use container(). |
- RenderObject* o = parent(); |
- |
- if (isText()) |
- return o; |
- |
- EPosition pos = m_style->position(); |
- if (pos == FixedPosition) { |
- // container() can be called on an object that is not in the |
- // tree yet. We don't call view() since it will assert if it |
- // can't get back to the canvas. Instead we just walk as high up |
- // as we can. If we're in the tree, we'll get the root. If we |
- // aren't we'll get the root of our little subtree (most likely |
- // we'll just return 0). |
- // FIXME: The definition of view() has changed to not crawl up the render tree. It might |
- // be safe now to use it. |
- while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) |
- o = o->parent(); |
- } else if (pos == AbsolutePosition) { |
- // Same goes here. We technically just want our containing block, but |
- // we may not have one if we're part of an uninstalled subtree. We'll |
- // climb as high as we can though. |
- while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) |
- o = o->parent(); |
- } |
- |
- return o; |
-} |
- |
-bool RenderObject::isSelectionBorder() const |
-{ |
- SelectionState st = selectionState(); |
- return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; |
-} |
- |
-void RenderObject::destroy() |
-{ |
- // Destroy any leftover anonymous children. |
- RenderObjectChildList* children = virtualChildren(); |
- if (children) |
- children->destroyLeftoverChildren(); |
- |
- // If this renderer is being autoscrolled, stop the autoscroll timer |
- if (document()->frame() && document()->frame()->eventHandler()->autoscrollRenderer() == this) |
- document()->frame()->eventHandler()->stopAutoscrollTimer(true); |
- |
- if (m_hasCounterNodeMap) |
- RenderCounter::destroyCounterNodes(this); |
- |
- if (AXObjectCache::accessibilityEnabled()) { |
- document()->axObjectCache()->childrenChanged(this->parent()); |
- document()->axObjectCache()->remove(this); |
- } |
- animation()->cancelAnimations(this); |
- |
- // By default no ref-counting. RenderWidget::destroy() doesn't call |
- // this function because it needs to do ref-counting. If anything |
- // in this function changes, be sure to fix RenderWidget::destroy() as well. |
- |
- remove(); |
- |
- // FIXME: Would like to do this in RenderBox, but the timing is so complicated that this can't easily |
- // be moved into RenderBox::destroy. |
- RenderArena* arena = renderArena(); |
- if (hasLayer()) |
- toRenderBox(this)->layer()->destroy(arena); |
- arenaDelete(arena, this); |
-} |
- |
-void RenderObject::arenaDelete(RenderArena* arena, void* base) |
-{ |
- if (m_style) { |
- for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { |
- if (StyleImage* backgroundImage = bgLayer->image()) |
- backgroundImage->removeClient(this); |
- } |
- |
- for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { |
- if (StyleImage* maskImage = maskLayer->image()) |
- maskImage->removeClient(this); |
- } |
- |
- if (StyleImage* borderImage = m_style->borderImage().image()) |
- borderImage->removeClient(this); |
- |
- if (StyleImage* maskBoxImage = m_style->maskBoxImage().image()) |
- maskBoxImage->removeClient(this); |
- } |
- |
-#ifndef NDEBUG |
- void* savedBase = baseOfRenderObjectBeingDeleted; |
- baseOfRenderObjectBeingDeleted = base; |
-#endif |
- delete this; |
-#ifndef NDEBUG |
- baseOfRenderObjectBeingDeleted = savedBase; |
-#endif |
- |
- // Recover the size left there for us by operator delete and free the memory. |
- arena->free(*(size_t*)base, base); |
-} |
- |
-VisiblePosition RenderObject::positionForCoordinates(int, int) |
-{ |
- return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); |
-} |
- |
-VisiblePosition RenderObject::positionForPoint(const IntPoint& point) |
-{ |
- return positionForCoordinates(point.x(), point.y()); |
-} |
- |
-void RenderObject::updateDragState(bool dragOn) |
-{ |
- bool valueChanged = (dragOn != m_isDragging); |
- m_isDragging = dragOn; |
- if (valueChanged && style()->affectedByDragRules()) |
- element()->setChanged(); |
- for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
- curr->updateDragState(dragOn); |
-} |
- |
-bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter) |
-{ |
- bool inside = false; |
- if (hitTestFilter != HitTestSelf) { |
- // First test the foreground layer (lines and inlines). |
- inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestForeground); |
- |
- // Test floats next. |
- if (!inside) |
- inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestFloat); |
- |
- // Finally test to see if the mouse is in the background (within a child block's background). |
- if (!inside) |
- inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestChildBlockBackgrounds); |
- } |
- |
- // See if the mouse is inside us but not any of our descendants |
- if (hitTestFilter != HitTestDescendants && !inside) |
- inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestBlockBackground); |
- |
- return inside; |
-} |
- |
-void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& point) |
-{ |
- if (result.innerNode()) |
- return; |
- |
- Node* node = element(); |
- if (node) { |
- result.setInnerNode(node); |
- if (!result.innerNonSharedNode()) |
- result.setInnerNonSharedNode(node); |
- result.setLocalPoint(point); |
- } |
-} |
- |
-bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, int /*y*/, int /*tx*/, int /*ty*/, HitTestAction) |
-{ |
- return false; |
-} |
- |
-int RenderObject::verticalPositionHint(bool firstLine) const |
-{ |
- if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. |
- firstLine = document()->usesFirstLineRules(); |
- int vpos = m_verticalPosition; |
- if (m_verticalPosition == PositionUndefined || firstLine) { |
- vpos = getVerticalPosition(firstLine); |
- if (!firstLine) |
- m_verticalPosition = vpos; |
- } |
- |
- return vpos; |
-} |
- |
-int RenderObject::getVerticalPosition(bool firstLine) const |
-{ |
- if (!isInline()) |
- return 0; |
- |
- // This method determines the vertical position for inline elements. |
- int vpos = 0; |
- EVerticalAlign va = style()->verticalAlign(); |
- if (va == TOP) |
- vpos = PositionTop; |
- else if (va == BOTTOM) |
- vpos = PositionBottom; |
- else { |
- bool checkParent = parent()->isInline() && !parent()->isInlineBlockOrInlineTable() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM; |
- vpos = checkParent ? parent()->verticalPositionHint(firstLine) : 0; |
- // don't allow elements nested inside text-top to have a different valignment. |
- if (va == BASELINE) |
- return vpos; |
- |
- const Font& f = parent()->style(firstLine)->font(); |
- int fontsize = f.pixelSize(); |
- |
- if (va == SUB) |
- vpos += fontsize / 5 + 1; |
- else if (va == SUPER) |
- vpos -= fontsize / 3 + 1; |
- else if (va == TEXT_TOP) |
- vpos += baselinePosition(firstLine) - f.ascent(); |
- else if (va == MIDDLE) |
- vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); |
- else if (va == TEXT_BOTTOM) { |
- vpos += f.descent(); |
- if (!isReplaced()) |
- vpos -= style(firstLine)->font().descent(); |
- } else if (va == BASELINE_MIDDLE) |
- vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); |
- else if (va == LENGTH) |
- vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); |
- } |
- |
- return vpos; |
-} |
- |
-int RenderObject::lineHeight(bool firstLine, bool /*isRootLineBox*/) const |
-{ |
- return style(firstLine)->computedLineHeight(); |
-} |
- |
-int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const |
-{ |
- const Font& f = style(firstLine)->font(); |
- return f.ascent() + (lineHeight(firstLine, isRootLineBox) - f.height()) / 2; |
-} |
- |
-void RenderObject::scheduleRelayout() |
-{ |
- if (isRenderView()) { |
- FrameView* view = toRenderView(this)->frameView(); |
- if (view) |
- view->scheduleRelayout(); |
- } else if (parent()) { |
- FrameView* v = view() ? view()->frameView() : 0; |
- if (v) |
- v->scheduleRelayoutOfSubtree(this); |
- } |
-} |
- |
-void RenderObject::layout() |
-{ |
- ASSERT(needsLayout()); |
- RenderObject* child = firstChild(); |
- while (child) { |
- child->layoutIfNeeded(); |
- ASSERT(!child->needsLayout()); |
- child = child->nextSibling(); |
- } |
- setNeedsLayout(false); |
-} |
- |
-InlineBox* RenderObject::createInlineBox(bool, bool unusedIsRootLineBox, bool) |
-{ |
- ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox); |
- return new (renderArena()) InlineBox(this); |
-} |
- |
-void RenderObject::dirtyLineBoxes(bool, bool) |
-{ |
-} |
- |
-InlineBox* RenderObject::inlineBoxWrapper() const |
-{ |
- return 0; |
-} |
- |
-void RenderObject::setInlineBoxWrapper(InlineBox*) |
-{ |
-} |
- |
-void RenderObject::deleteLineBoxWrapper() |
-{ |
-} |
- |
-RenderStyle* RenderObject::firstLineStyleSlowCase() const |
-{ |
- ASSERT(document()->usesFirstLineRules()); |
- |
- RenderStyle* style = m_style.get(); |
- const RenderObject* renderer = isText() ? parent() : this; |
- if (renderer->isBlockFlow()) { |
- if (RenderBlock* firstLineBlock = renderer->firstLineBlock()) |
- style = firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); |
- } else if (!renderer->isAnonymous() && renderer->isRenderInline()) { |
- RenderStyle* parentStyle = renderer->parent()->firstLineStyle(); |
- if (parentStyle != renderer->parent()->style()) { |
- // A first-line style is in effect. Cache a first-line style for ourselves. |
- style->setHasPseudoStyle(FIRST_LINE_INHERITED); |
- style = renderer->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); |
- } |
- } |
- |
- return style; |
-} |
- |
-RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const |
-{ |
- if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) |
- return 0; |
- |
- RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo); |
- if (cachedStyle) |
- return cachedStyle; |
- |
- RefPtr<RenderStyle> result = getUncachedPseudoStyle(pseudo, parentStyle); |
- if (result) |
- return style()->addCachedPseudoStyle(result.release()); |
- return 0; |
-} |
- |
-PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const |
-{ |
- if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) |
- return 0; |
- |
- if (!parentStyle) |
- parentStyle = style(); |
- |
- Node* node = element(); |
- while (node && !node->isElementNode()) |
- node = node->parentNode(); |
- if (!node) |
- return 0; |
- |
- RefPtr<RenderStyle> result; |
- if (pseudo == FIRST_LINE_INHERITED) { |
- result = document()->styleSelector()->styleForElement(static_cast<Element*>(node), parentStyle, false); |
- result->setStyleType(FIRST_LINE_INHERITED); |
- } else |
- result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(node), parentStyle); |
- return result.release(); |
-} |
- |
-static Color decorationColor(RenderStyle* style) |
-{ |
- Color result; |
- if (style->textStrokeWidth() > 0) { |
- // Prefer stroke color if possible but not if it's fully transparent. |
- result = style->textStrokeColor(); |
- if (!result.isValid()) |
- result = style->color(); |
- if (result.alpha()) |
- return result; |
- } |
- |
- result = style->textFillColor(); |
- if (!result.isValid()) |
- result = style->color(); |
- return result; |
-} |
- |
-void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, |
- Color& linethrough, bool quirksMode) |
-{ |
- RenderObject* curr = this; |
- do { |
- int currDecs = curr->style()->textDecoration(); |
- if (currDecs) { |
- if (currDecs & UNDERLINE) { |
- decorations &= ~UNDERLINE; |
- underline = decorationColor(curr->style()); |
- } |
- if (currDecs & OVERLINE) { |
- decorations &= ~OVERLINE; |
- overline = decorationColor(curr->style()); |
- } |
- if (currDecs & LINE_THROUGH) { |
- decorations &= ~LINE_THROUGH; |
- linethrough = decorationColor(curr->style()); |
- } |
- } |
- curr = curr->parent(); |
- if (curr && curr->isRenderBlock() && toRenderBlock(curr)->inlineContinuation()) |
- curr = toRenderBlock(curr)->inlineContinuation(); |
- } while (curr && decorations && (!quirksMode || !curr->element() || |
- (!curr->element()->hasTagName(aTag) && !curr->element()->hasTagName(fontTag)))); |
- |
- // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). |
- if (decorations && curr) { |
- if (decorations & UNDERLINE) |
- underline = decorationColor(curr->style()); |
- if (decorations & OVERLINE) |
- overline = decorationColor(curr->style()); |
- if (decorations & LINE_THROUGH) |
- linethrough = decorationColor(curr->style()); |
- } |
-} |
- |
-void RenderObject::updateWidgetPosition() |
-{ |
-} |
- |
-#if ENABLE(DASHBOARD_SUPPORT) |
-void RenderObject::addDashboardRegions(Vector<DashboardRegionValue>& regions) |
-{ |
- // Convert the style regions to absolute coordinates. |
- if (style()->visibility() != VISIBLE || !isBox()) |
- return; |
- |
- RenderBox* box = toRenderBox(this); |
- |
- const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); |
- unsigned i, count = styleRegions.size(); |
- for (i = 0; i < count; i++) { |
- StyleDashboardRegion styleRegion = styleRegions[i]; |
- |
- int w = box->width(); |
- int h = box->height(); |
- |
- DashboardRegionValue region; |
- region.label = styleRegion.label; |
- region.bounds = IntRect(styleRegion.offset.left().value(), |
- styleRegion.offset.top().value(), |
- w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), |
- h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); |
- region.type = styleRegion.type; |
- |
- region.clip = region.bounds; |
- computeAbsoluteRepaintRect(region.clip); |
- if (region.clip.height() < 0) { |
- region.clip.setHeight(0); |
- region.clip.setWidth(0); |
- } |
- |
- FloatPoint absPos = localToAbsolute(); |
- region.bounds.setX(absPos.x() + styleRegion.offset.left().value()); |
- region.bounds.setY(absPos.y() + styleRegion.offset.top().value()); |
- |
- if (document()->frame()) { |
- float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); |
- if (pageScaleFactor != 1.0f) { |
- region.bounds.scale(pageScaleFactor); |
- region.clip.scale(pageScaleFactor); |
- } |
- } |
- |
- regions.append(region); |
- } |
-} |
- |
-void RenderObject::collectDashboardRegions(Vector<DashboardRegionValue>& regions) |
-{ |
- // RenderTexts don't have their own style, they just use their parent's style, |
- // so we don't want to include them. |
- if (isText()) |
- return; |
- |
- addDashboardRegions(regions); |
- for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
- curr->collectDashboardRegions(regions); |
-} |
-#endif |
- |
-bool RenderObject::avoidsFloats() const |
-{ |
- return isReplaced() || hasOverflowClip() || isHR(); |
-} |
- |
-bool RenderObject::shrinkToAvoidFloats() const |
-{ |
- // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this |
- // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the |
- // current remaining width on a line. |
- if (isInline() && !isHTMLMarquee() || !avoidsFloats()) |
- return false; |
- |
- // All auto-width objects that avoid floats should always use lineWidth. |
- return style()->width().isAuto(); |
-} |
- |
-bool RenderObject::willRenderImage(CachedImage*) |
-{ |
- // Without visibility we won't render (and therefore don't care about animation). |
- if (style()->visibility() != VISIBLE) |
- return false; |
- |
- // If we're not in a window (i.e., we're dormant from being put in the b/f cache or in a background tab) |
- // then we don't want to render either. |
- return !document()->inPageCache() && !document()->view()->isOffscreen(); |
-} |
- |
-int RenderObject::maximalOutlineSize(PaintPhase p) const |
-{ |
- if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) |
- return 0; |
- return toRenderView(document()->renderer())->maximalOutlineSize(); |
-} |
- |
-int RenderObject::caretMinOffset() const |
-{ |
- return 0; |
-} |
- |
-int RenderObject::caretMaxOffset() const |
-{ |
- if (isReplaced()) |
- return element() ? max(1U, element()->childNodeCount()) : 1; |
- if (isHR()) |
- return 1; |
- return 0; |
-} |
- |
-unsigned RenderObject::caretMaxRenderedOffset() const |
-{ |
- return 0; |
-} |
- |
-int RenderObject::previousOffset(int current) const |
-{ |
- return current - 1; |
-} |
- |
-int RenderObject::previousOffsetForBackwardDeletion(int current) const |
-{ |
- return current - 1; |
-} |
- |
-int RenderObject::nextOffset(int current) const |
-{ |
- return current + 1; |
-} |
- |
-void RenderObject::adjustRectForOutlineAndShadow(IntRect& rect) const |
-{ |
- int outlineSize = outlineStyleForRepaint()->outlineSize(); |
- if (ShadowData* boxShadow = style()->boxShadow()) { |
- int shadowLeft = 0; |
- int shadowRight = 0; |
- int shadowTop = 0; |
- int shadowBottom = 0; |
- |
- do { |
- shadowLeft = min(boxShadow->x - boxShadow->blur - outlineSize, shadowLeft); |
- shadowRight = max(boxShadow->x + boxShadow->blur + outlineSize, shadowRight); |
- shadowTop = min(boxShadow->y - boxShadow->blur - outlineSize, shadowTop); |
- shadowBottom = max(boxShadow->y + boxShadow->blur + outlineSize, shadowBottom); |
- |
- boxShadow = boxShadow->next; |
- } while (boxShadow); |
- |
- rect.move(shadowLeft, shadowTop); |
- rect.setWidth(rect.width() - shadowLeft + shadowRight); |
- rect.setHeight(rect.height() - shadowTop + shadowBottom); |
- } else |
- rect.inflate(outlineSize); |
-} |
- |
-AnimationController* RenderObject::animation() const |
-{ |
- return document()->frame()->animation(); |
-} |
- |
-void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) |
-{ |
- imageChanged(static_cast<WrappedImagePtr>(image), rect); |
-} |
- |
-#if ENABLE(SVG) |
- |
-FloatRect RenderObject::relativeBBox(bool) const |
-{ |
- return FloatRect(); |
-} |
- |
-TransformationMatrix RenderObject::localTransform() const |
-{ |
- return TransformationMatrix(); |
-} |
- |
-TransformationMatrix RenderObject::absoluteTransform() const |
-{ |
- if (parent()) |
- return localTransform() * parent()->absoluteTransform(); |
- return localTransform(); |
-} |
- |
-#endif // ENABLE(SVG) |
- |
-} // namespace WebCore |
- |
-#ifndef NDEBUG |
- |
-void showTree(const WebCore::RenderObject* ro) |
-{ |
- if (ro) |
- ro->showTreeForThis(); |
-} |
- |
-#endif |
- |
+/* |
+ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
+ * (C) 1999 Antti Koivisto (koivisto@kde.org) |
+ * (C) 2000 Dirk Mueller (mueller@kde.org) |
+ * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) |
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple 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 "RenderObject.h" |
+ |
+#include "AXObjectCache.h" |
+#include "CSSStyleSelector.h" |
+#include "FloatQuad.h" |
+#include "FrameView.h" |
+#include "GraphicsContext.h" |
+#include "HTMLNames.h" |
+#include "HitTestResult.h" |
+#include "Page.h" |
+#include "RenderArena.h" |
+#include "RenderCounter.h" |
+#include "RenderFlexibleBox.h" |
+#include "RenderImageGeneratedContent.h" |
+#include "RenderInline.h" |
+#include "RenderListItem.h" |
+#include "RenderTableCell.h" |
+#include "RenderTableCol.h" |
+#include "RenderTableRow.h" |
+#include "RenderTheme.h" |
+#include "RenderView.h" |
+#include <algorithm> |
+#include <stdio.h> |
+#include <wtf/RefCountedLeakCounter.h> |
+ |
+#if ENABLE(WML) |
+#include "WMLNames.h" |
+#endif |
+ |
+using namespace std; |
+ |
+namespace WebCore { |
+ |
+using namespace HTMLNames; |
+ |
+#ifndef NDEBUG |
+static void* baseOfRenderObjectBeingDeleted; |
+#endif |
+ |
+bool RenderObject::s_affectsParentBlock = false; |
+ |
+void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw() |
+{ |
+ return renderArena->allocate(sz); |
+} |
+ |
+void RenderObject::operator delete(void* ptr, size_t sz) |
+{ |
+ ASSERT(baseOfRenderObjectBeingDeleted == ptr); |
+ |
+ // Stash size where destroy can find it. |
+ *(size_t *)ptr = sz; |
+} |
+ |
+RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) |
+{ |
+ Document* doc = node->document(); |
+ RenderArena* arena = doc->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->m_next && contentData->m_type == CONTENT_OBJECT && doc != node) { |
+ RenderImageGeneratedContent* image = new (arena) RenderImageGeneratedContent(node); |
+ image->setStyle(style); |
+ if (StyleImage* styleImage = contentData->m_content.m_image) |
+ image->setStyleImage(styleImage); |
+ return image; |
+ } |
+ |
+ RenderObject* o = 0; |
+ |
+ switch (style->display()) { |
+ case NONE: |
+ break; |
+ case INLINE: |
+ o = new (arena) RenderInline(node); |
+ break; |
+ case BLOCK: |
+ o = new (arena) RenderBlock(node); |
+ break; |
+ case INLINE_BLOCK: |
+ o = new (arena) RenderBlock(node); |
+ break; |
+ case LIST_ITEM: |
+ o = new (arena) RenderListItem(node); |
+ break; |
+ case RUN_IN: |
+ case COMPACT: |
+ o = new (arena) RenderBlock(node); |
+ break; |
+ case TABLE: |
+ case INLINE_TABLE: |
+ o = new (arena) RenderTable(node); |
+ break; |
+ case TABLE_ROW_GROUP: |
+ case TABLE_HEADER_GROUP: |
+ case TABLE_FOOTER_GROUP: |
+ o = new (arena) RenderTableSection(node); |
+ break; |
+ case TABLE_ROW: |
+ o = new (arena) RenderTableRow(node); |
+ break; |
+ case TABLE_COLUMN_GROUP: |
+ case TABLE_COLUMN: |
+ o = new (arena) RenderTableCol(node); |
+ break; |
+ case TABLE_CELL: |
+ o = new (arena) RenderTableCell(node); |
+ break; |
+ case TABLE_CAPTION: |
+ o = new (arena) RenderBlock(node); |
+ break; |
+ case BOX: |
+ case INLINE_BOX: |
+ o = new (arena) RenderFlexibleBox(node); |
+ break; |
+ } |
+ |
+ return o; |
+} |
+ |
+#ifndef NDEBUG |
+static WTF::RefCountedLeakCounter renderObjectCounter("RenderObject"); |
+#endif |
+ |
+RenderObject::RenderObject(Node* node) |
+ : CachedResourceClient() |
+ , m_style(0) |
+ , m_node(node) |
+ , m_parent(0) |
+ , m_previous(0) |
+ , m_next(0) |
+#ifndef NDEBUG |
+ , m_hasAXObject(false) |
+ , m_setNeedsLayoutForbidden(false) |
+#endif |
+ , m_verticalPosition(PositionUndefined) |
+ , m_needsLayout(false) |
+ , m_needsPositionedMovementLayout(false) |
+ , m_normalChildNeedsLayout(false) |
+ , m_posChildNeedsLayout(false) |
+ , m_prefWidthsDirty(false) |
+ , m_floating(false) |
+ , m_positioned(false) |
+ , m_relPositioned(false) |
+ , m_paintBackground(false) |
+ , m_isAnonymous(node == node->document()) |
+ , m_isText(false) |
+ , m_isBox(false) |
+ , m_inline(true) |
+ , m_replaced(false) |
+ , m_isDragging(false) |
+ , m_hasLayer(false) |
+ , m_hasOverflowClip(false) |
+ , m_hasTransform(false) |
+ , m_hasReflection(false) |
+ , m_hasOverrideSize(false) |
+ , m_hasCounterNodeMap(false) |
+ , m_everHadLayout(false) |
+ , m_childrenInline(false) |
+ , m_topMarginQuirk(false) |
+ , m_bottomMarginQuirk(false) |
+ , m_hasMarkupTruncation(false) |
+ , m_selectionState(SelectionNone) |
+ , m_hasColumns(false) |
+ , m_cellWidthChanged(false) |
+{ |
+#ifndef NDEBUG |
+ renderObjectCounter.increment(); |
+#endif |
+} |
+ |
+RenderObject::~RenderObject() |
+{ |
+ ASSERT(!node() || documentBeingDestroyed() || !document()->frame()->view() || document()->frame()->view()->layoutRoot() != this); |
+#ifndef NDEBUG |
+ ASSERT(!m_hasAXObject); |
+ renderObjectCounter.decrement(); |
+#endif |
+} |
+ |
+bool RenderObject::isDescendantOf(const RenderObject* obj) const |
+{ |
+ for (const RenderObject* r = this; r; r = r->m_parent) { |
+ if (r == obj) |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool RenderObject::isBody() const |
+{ |
+ return node()->hasTagName(bodyTag); |
+} |
+ |
+bool RenderObject::isHR() const |
+{ |
+ return element() && element()->hasTagName(hrTag); |
+} |
+ |
+bool RenderObject::isHTMLMarquee() const |
+{ |
+ return element() && element()->renderer() == this && element()->hasTagName(marqueeTag); |
+} |
+ |
+ |
+static void updateListMarkerNumbers(RenderObject* child) |
+{ |
+ for (RenderObject* r = child; r; r = r->nextSibling()) |
+ if (r->isListItem()) |
+ static_cast<RenderListItem*>(r)->updateValue(); |
+} |
+ |
+void RenderObject::addChild(RenderObject* newChild, RenderObject* beforeChild) |
+{ |
+ RenderObjectChildList* children = virtualChildren(); |
+ ASSERT(children); |
+ if (!children) |
+ return; |
+ |
+ bool needsTable = false; |
+ |
+ if (newChild->isListItem()) |
+ updateListMarkerNumbers(beforeChild ? beforeChild : children->lastChild()); |
+ else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) |
+ needsTable = !isTable(); |
+ else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) |
+ needsTable = !isTable(); |
+ else if (newChild->isTableSection()) |
+ needsTable = !isTable(); |
+ else if (newChild->isTableRow()) |
+ needsTable = !isTableSection(); |
+ else if (newChild->isTableCell()) { |
+ needsTable = !isTableRow(); |
+ // I'm not 100% sure this is the best way to fix this, but without this |
+ // change we recurse infinitely when trying to render the CSS2 test page: |
+ // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. |
+ // See Radar 2925291. |
+ if (needsTable && isTableCell() && !children->firstChild() && !newChild->isTableCell()) |
+ needsTable = false; |
+ } |
+ |
+ if (needsTable) { |
+ RenderTable* table; |
+ RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : children->lastChild(); |
+ if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) |
+ table = static_cast<RenderTable*>(afterChild); |
+ else { |
+ table = new (renderArena()) RenderTable(document() /* is anonymous */); |
+ RefPtr<RenderStyle> newStyle = RenderStyle::create(); |
+ newStyle->inheritFrom(style()); |
+ newStyle->setDisplay(TABLE); |
+ table->setStyle(newStyle.release()); |
+ addChild(table, beforeChild); |
+ } |
+ table->addChild(newChild); |
+ } else { |
+ // Just add it... |
+ children->insertChildNode(this, newChild, beforeChild); |
+ } |
+ |
+ if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { |
+ RefPtr<StringImpl> textToTransform = toRenderText(newChild)->originalText(); |
+ if (textToTransform) |
+ toRenderText(newChild)->setText(textToTransform.release(), true); |
+ } |
+} |
+ |
+void RenderObject::removeChild(RenderObject* oldChild) |
+{ |
+ RenderObjectChildList* children = virtualChildren(); |
+ ASSERT(children); |
+ if (!children) |
+ return; |
+ |
+ // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode |
+ // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on |
+ // layout anyway). |
+ if (oldChild->isFloatingOrPositioned()) |
+ toRenderBox(oldChild)->removeFloatingOrPositionedChildFromBlockLists(); |
+ |
+ children->removeChildNode(this, oldChild); |
+} |
+ |
+RenderObject* RenderObject::nextInPreOrder() const |
+{ |
+ if (RenderObject* o = firstChild()) |
+ return o; |
+ |
+ return nextInPreOrderAfterChildren(); |
+} |
+ |
+RenderObject* RenderObject::nextInPreOrderAfterChildren() const |
+{ |
+ RenderObject* o; |
+ if (!(o = nextSibling())) { |
+ o = parent(); |
+ while (o && !o->nextSibling()) |
+ o = o->parent(); |
+ if (o) |
+ o = o->nextSibling(); |
+ } |
+ |
+ return o; |
+} |
+ |
+RenderObject* RenderObject::nextInPreOrder(RenderObject* stayWithin) const |
+{ |
+ if (RenderObject* o = firstChild()) |
+ return o; |
+ |
+ return nextInPreOrderAfterChildren(stayWithin); |
+} |
+ |
+RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin) const |
+{ |
+ if (this == stayWithin) |
+ return 0; |
+ |
+ RenderObject* o; |
+ if (!(o = nextSibling())) { |
+ o = parent(); |
+ while (o && !o->nextSibling()) { |
+ if (o == stayWithin) |
+ return 0; |
+ o = o->parent(); |
+ } |
+ if (o) |
+ o = o->nextSibling(); |
+ } |
+ |
+ return o; |
+} |
+ |
+RenderObject* RenderObject::previousInPreOrder() const |
+{ |
+ if (RenderObject* o = previousSibling()) { |
+ while (o->lastChild()) |
+ o = o->lastChild(); |
+ return o; |
+ } |
+ |
+ return parent(); |
+} |
+ |
+RenderObject* RenderObject::childAt(unsigned index) const |
+{ |
+ RenderObject* child = firstChild(); |
+ for (unsigned i = 0; child && i < index; i++) |
+ child = child->nextSibling(); |
+ return child; |
+} |
+ |
+bool RenderObject::isEditable() const |
+{ |
+ RenderText* textRenderer = 0; |
+ if (isText()) |
+ textRenderer = toRenderText(const_cast<RenderObject*>(this)); |
+ |
+ return style()->visibility() == VISIBLE && |
+ element() && element()->isContentEditable() && |
+ ((isBlockFlow() && !firstChild()) || |
+ isReplaced() || |
+ isBR() || |
+ (textRenderer && textRenderer->firstTextBox())); |
+} |
+ |
+RenderObject* RenderObject::firstLeafChild() const |
+{ |
+ RenderObject* r = firstChild(); |
+ while (r) { |
+ RenderObject* n = 0; |
+ n = r->firstChild(); |
+ if (!n) |
+ break; |
+ r = n; |
+ } |
+ return r; |
+} |
+ |
+RenderObject* RenderObject::lastLeafChild() const |
+{ |
+ RenderObject* r = lastChild(); |
+ while (r) { |
+ RenderObject* n = 0; |
+ n = r->lastChild(); |
+ if (!n) |
+ break; |
+ r = n; |
+ } |
+ return r; |
+} |
+ |
+static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& 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 = 0; |
+ } |
+ parentLayer->addChild(toRenderBox(obj)->layer(), beforeChild); |
+ return; |
+ } |
+ |
+ for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) |
+ addLayers(curr, parentLayer, newObject, beforeChild); |
+} |
+ |
+void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject) |
+{ |
+ if (!parentLayer) |
+ return; |
+ |
+ RenderObject* object = newObject; |
+ RenderLayer* beforeChild = 0; |
+ WebCore::addLayers(this, parentLayer, object, beforeChild); |
+} |
+ |
+void RenderObject::removeLayers(RenderLayer* parentLayer) |
+{ |
+ if (!parentLayer) |
+ return; |
+ |
+ if (hasLayer()) { |
+ parentLayer->removeChild(toRenderBox(this)->layer()); |
+ return; |
+ } |
+ |
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
+ curr->removeLayers(parentLayer); |
+} |
+ |
+void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) |
+{ |
+ if (!newParent) |
+ return; |
+ |
+ if (hasLayer()) { |
+ RenderLayer* layer = toRenderBox(this)->layer(); |
+ if (oldParent) |
+ oldParent->removeChild(layer); |
+ newParent->addChild(layer); |
+ return; |
+ } |
+ |
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
+ curr->moveLayers(oldParent, newParent); |
+} |
+ |
+RenderLayer* RenderObject::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() ? toRenderBox(this)->layer() : 0; |
+ 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* curr = startPoint ? startPoint->nextSibling() : firstChild(); |
+ curr; curr = curr->nextSibling()) { |
+ RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, 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 0; |
+ |
+ // 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 0; |
+} |
+ |
+RenderLayer* RenderObject::enclosingLayer() const |
+{ |
+ const RenderObject* curr = this; |
+ while (curr) { |
+ RenderLayer* layer = curr->hasLayer() ? toRenderBox(curr)->layer() : 0; |
+ if (layer) |
+ return layer; |
+ curr = curr->parent(); |
+ } |
+ return 0; |
+} |
+ |
+#if USE(ACCELERATED_COMPOSITING) |
+RenderLayer* RenderObject::enclosingCompositingLayer() const |
+{ |
+ const RenderObject* curr = this; |
+ while (curr) { |
+ RenderLayer* layer = curr->hasLayer() ? toRenderBox(curr)->layer() : 0; |
+ if (layer && layer->isComposited()) |
+ return layer; |
+ curr = curr->parent(); |
+ } |
+ return 0; |
+} |
+#endif |
+ |
+RenderBox* RenderObject::enclosingBox() const |
+{ |
+ RenderObject* curr = const_cast<RenderObject*>(this); |
+ while (curr) { |
+ if (curr->isBox()) |
+ return toRenderBox(curr); |
+ curr = curr->parent(); |
+ } |
+ |
+ ASSERT_NOT_REACHED(); |
+ return 0; |
+} |
+ |
+RenderBlock* RenderObject::firstLineBlock() const |
+{ |
+ return 0; |
+} |
+ |
+bool RenderObject::hasStaticX() const |
+{ |
+ return (style()->left().isAuto() && style()->right().isAuto()) || style()->left().isStatic() || style()->right().isStatic(); |
+} |
+ |
+bool RenderObject::hasStaticY() const |
+{ |
+ return (style()->top().isAuto() && style()->bottom().isAuto()) || style()->top().isStatic(); |
+} |
+ |
+void RenderObject::setPrefWidthsDirty(bool b, bool markParents) |
+{ |
+ bool alreadyDirty = m_prefWidthsDirty; |
+ m_prefWidthsDirty = b; |
+ if (b && !alreadyDirty && markParents && (isText() || (style()->position() != FixedPosition && style()->position() != AbsolutePosition))) |
+ invalidateContainerPrefWidths(); |
+} |
+ |
+void RenderObject::invalidateContainerPrefWidths() |
+{ |
+ // In order to avoid pathological behavior when inlines are deeply nested, we do include them |
+ // in the chain that we mark dirty (even though they're kind of irrelevant). |
+ RenderObject* o = isTableCell() ? containingBlock() : container(); |
+ while (o && !o->m_prefWidthsDirty) { |
+ o->m_prefWidthsDirty = true; |
+ if (o->style()->position() == FixedPosition || o->style()->position() == AbsolutePosition) |
+ // A positioned object has no effect on the min/max width of its containing block ever. |
+ // We can optimize this case and not go up any further. |
+ break; |
+ o = o->isTableCell() ? o->containingBlock() : o->container(); |
+ } |
+} |
+ |
+void RenderObject::setLayerNeedsFullRepaint() |
+{ |
+ toRenderBox(this)->layer()->setNeedsFullRepaint(true); |
+} |
+ |
+RenderBlock* RenderObject::containingBlock() const |
+{ |
+ if (isTableCell()) { |
+ const RenderTableCell* cell = static_cast<const RenderTableCell*>(this); |
+ if (parent() && cell->section()) |
+ return cell->table(); |
+ return 0; |
+ } |
+ |
+ if (isRenderView()) |
+ return const_cast<RenderBlock*>(static_cast<const RenderBlock*>(this)); |
+ |
+ RenderObject* o = parent(); |
+ if (!isText() && m_style->position() == FixedPosition) { |
+ while (o && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) |
+ o = o->parent(); |
+ } else if (!isText() && m_style->position() == AbsolutePosition) { |
+ while (o && (o->style()->position() == StaticPosition || (o->isInline() && !o->isReplaced())) && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { |
+ // For relpositioned inlines, we return the nearest enclosing block. We don't try |
+ // to return the inline itself. This allows us to avoid having a positioned objects |
+ // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result |
+ // from this method. The container() method can actually be used to obtain the |
+ // inline directly. |
+ if (o->style()->position() == RelativePosition && o->isInline() && !o->isReplaced()) |
+ return o->containingBlock(); |
+ o = o->parent(); |
+ } |
+ } else { |
+ while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection() |
+ || o->isTableCol() || o->isFrameSet() || o->isMedia() |
+#if ENABLE(SVG) |
+ || o->isSVGContainer() || o->isSVGRoot() |
+#endif |
+ )) |
+ o = o->parent(); |
+ } |
+ |
+ if (!o || !o->isRenderBlock()) |
+ return 0; // Probably doesn't happen any more, but leave just in case. -dwh |
+ |
+ return toRenderBlock(o); |
+} |
+ |
+int RenderObject::containingBlockWidth() const |
+{ |
+ return containingBlock()->availableWidth(); |
+} |
+ |
+int RenderObject::containingBlockHeight() const |
+{ |
+ return containingBlock()->contentHeight(); |
+} |
+ |
+static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) |
+{ |
+ // Nobody will use multiple layers without wanting fancy positioning. |
+ if (layer->next()) |
+ return true; |
+ |
+ // Make sure we have a valid image. |
+ StyleImage* img = layer->image(); |
+ bool shouldPaintBackgroundImage = img && img->canRender(renderer->style()->effectiveZoom()); |
+ |
+ // These are always percents or auto. |
+ if (shouldPaintBackgroundImage && |
+ (!layer->xPosition().isZero() || !layer->yPosition().isZero() || |
+ layer->size().width().isPercent() || layer->size().height().isPercent())) |
+ // The image will shift unpredictably if the size changes. |
+ return true; |
+ |
+ return false; |
+} |
+ |
+bool RenderObject::mustRepaintBackgroundOrBorder() const |
+{ |
+ if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers())) |
+ return true; |
+ |
+ // If we don't have a background/border/mask, then nothing to do. |
+ if (!hasBoxDecorations()) |
+ return false; |
+ |
+ if (mustRepaintFillLayers(this, style()->backgroundLayers())) |
+ return true; |
+ |
+ // Our fill layers are ok. Let's check border. |
+ if (style()->hasBorder()) { |
+ // Border images are not ok. |
+ StyleImage* borderImage = style()->borderImage().image(); |
+ bool shouldPaintBorderImage = borderImage && borderImage->canRender(style()->effectiveZoom()); |
+ |
+ // If the image hasn't loaded, we're still using the normal border style. |
+ if (shouldPaintBorderImage && borderImage->isLoaded()) |
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+void RenderObject::drawBorderArc(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, |
+ int angleStart, int angleSpan, BorderSide s, Color c, const Color& textColor, |
+ EBorderStyle style, bool firstCorner) |
+{ |
+ if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2)) |
+ style = SOLID; |
+ |
+ if (!c.isValid()) { |
+ if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) |
+ c.setRGB(238, 238, 238); |
+ else |
+ c = textColor; |
+ } |
+ |
+ switch (style) { |
+ case BNONE: |
+ case BHIDDEN: |
+ return; |
+ case DOTTED: |
+ case DASHED: |
+ graphicsContext->setStrokeColor(c); |
+ graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); |
+ graphicsContext->setStrokeThickness(thickness); |
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
+ break; |
+ case DOUBLE: { |
+ float third = thickness / 3.0f; |
+ float innerThird = (thickness + 1.0f) / 6.0f; |
+ int shiftForInner = static_cast<int>(innerThird * 2.5f); |
+ |
+ int outerY = y; |
+ int outerHeight = radius.height() * 2; |
+ int innerX = x + shiftForInner; |
+ int innerY = y + shiftForInner; |
+ int innerWidth = (radius.width() - shiftForInner) * 2; |
+ int innerHeight = (radius.height() - shiftForInner) * 2; |
+ if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) { |
+ outerHeight += 2; |
+ innerHeight += 2; |
+ } |
+ |
+ graphicsContext->setStrokeStyle(SolidStroke); |
+ graphicsContext->setStrokeColor(c); |
+ graphicsContext->setStrokeThickness(third); |
+ graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); |
+ graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); |
+ graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan); |
+ break; |
+ } |
+ case GROOVE: |
+ case RIDGE: { |
+ Color c2; |
+ if ((style == RIDGE && (s == BSTop || s == BSLeft)) || |
+ (style == GROOVE && (s == BSBottom || s == BSRight))) |
+ c2 = c.dark(); |
+ else { |
+ c2 = c; |
+ c = c.dark(); |
+ } |
+ |
+ graphicsContext->setStrokeStyle(SolidStroke); |
+ graphicsContext->setStrokeColor(c); |
+ graphicsContext->setStrokeThickness(thickness); |
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
+ |
+ float halfThickness = (thickness + 1.0f) / 4.0f; |
+ int shiftForInner = static_cast<int>(halfThickness * 1.5f); |
+ graphicsContext->setStrokeColor(c2); |
+ graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); |
+ graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, |
+ (radius.height() - shiftForInner) * 2), angleStart, angleSpan); |
+ break; |
+ } |
+ case INSET: |
+ if (s == BSTop || s == BSLeft) |
+ c = c.dark(); |
+ case OUTSET: |
+ if (style == OUTSET && (s == BSBottom || s == BSRight)) |
+ c = c.dark(); |
+ case SOLID: |
+ graphicsContext->setStrokeStyle(SolidStroke); |
+ graphicsContext->setStrokeColor(c); |
+ graphicsContext->setStrokeThickness(thickness); |
+ graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); |
+ break; |
+ } |
+} |
+ |
+void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, |
+ BorderSide s, Color c, const Color& textcolor, EBorderStyle style, |
+ int adjbw1, int adjbw2) |
+{ |
+ int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1); |
+ |
+ if (style == DOUBLE && width < 3) |
+ style = SOLID; |
+ |
+ if (!c.isValid()) { |
+ if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) |
+ c.setRGB(238, 238, 238); |
+ else |
+ c = textcolor; |
+ } |
+ |
+ switch (style) { |
+ case BNONE: |
+ case BHIDDEN: |
+ return; |
+ case DOTTED: |
+ case DASHED: |
+ graphicsContext->setStrokeColor(c); |
+ graphicsContext->setStrokeThickness(width); |
+ graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); |
+ |
+ if (width > 0) |
+ switch (s) { |
+ case BSBottom: |
+ case BSTop: |
+ graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); |
+ break; |
+ case BSRight: |
+ case BSLeft: |
+ graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); |
+ break; |
+ } |
+ break; |
+ case DOUBLE: { |
+ int third = (width + 1) / 3; |
+ |
+ if (adjbw1 == 0 && adjbw2 == 0) { |
+ graphicsContext->setStrokeStyle(NoStroke); |
+ graphicsContext->setFillColor(c); |
+ switch (s) { |
+ case BSTop: |
+ case BSBottom: |
+ graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, third)); |
+ graphicsContext->drawRect(IntRect(x1, y2 - third, x2 - x1, third)); |
+ break; |
+ case BSLeft: |
+ graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); |
+ graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); |
+ break; |
+ case BSRight: |
+ graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); |
+ graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); |
+ break; |
+ } |
+ } else { |
+ int adjbw1bigthird = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 3; |
+ int adjbw2bigthird = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 3; |
+ |
+ switch (s) { |
+ case BSTop: |
+ drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), |
+ y1, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y1 + third, |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), |
+ y2 - third, x2 - max((adjbw2 * 2 + 1) / 3, 0), y2, |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ break; |
+ case BSLeft: |
+ drawBorder(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0), |
+ x1 + third, y2 - max((-adjbw2 * 2 + 1) / 3, 0), |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ drawBorder(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0), |
+ x2, y2 - max((adjbw2 * 2 + 1) / 3, 0), |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ break; |
+ case BSBottom: |
+ drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), |
+ y1, x2 - max((adjbw2 * 2 + 1) / 3, 0), y1 + third, |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), |
+ y2 - third, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y2, |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ break; |
+ case BSRight: |
+ drawBorder(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0), |
+ x1 + third, y2 - max(( adjbw2 * 2 + 1) / 3, 0), |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ drawBorder(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0), |
+ x2, y2 - max((-adjbw2 * 2 + 1) / 3, 0), |
+ s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); |
+ break; |
+ default: |
+ break; |
+ } |
+ } |
+ break; |
+ } |
+ case RIDGE: |
+ case GROOVE: |
+ { |
+ EBorderStyle s1; |
+ EBorderStyle s2; |
+ if (style == GROOVE) { |
+ s1 = INSET; |
+ s2 = OUTSET; |
+ } else { |
+ s1 = OUTSET; |
+ s2 = INSET; |
+ } |
+ |
+ int adjbw1bighalf = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 2; |
+ int adjbw2bighalf = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 2; |
+ |
+ switch (s) { |
+ case BSTop: |
+ drawBorder(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, |
+ s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); |
+ drawBorder(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2, |
+ s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); |
+ break; |
+ case BSLeft: |
+ drawBorder(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2, |
+ s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); |
+ drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2, |
+ s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); |
+ break; |
+ case BSBottom: |
+ drawBorder(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, |
+ s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); |
+ drawBorder(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2, |
+ s, c, textcolor, s1, adjbw1/2, adjbw2/2); |
+ break; |
+ case BSRight: |
+ drawBorder(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2, |
+ s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); |
+ drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2, |
+ s, c, textcolor, s1, adjbw1/2, adjbw2/2); |
+ break; |
+ } |
+ break; |
+ } |
+ case INSET: |
+ if (s == BSTop || s == BSLeft) |
+ c = c.dark(); |
+ // fall through |
+ case OUTSET: |
+ if (style == OUTSET && (s == BSBottom || s == BSRight)) |
+ c = c.dark(); |
+ // fall through |
+ case SOLID: { |
+ graphicsContext->setStrokeStyle(NoStroke); |
+ graphicsContext->setFillColor(c); |
+ ASSERT(x2 >= x1); |
+ ASSERT(y2 >= y1); |
+ if (!adjbw1 && !adjbw2) { |
+ graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); |
+ return; |
+ } |
+ FloatPoint quad[4]; |
+ switch (s) { |
+ case BSTop: |
+ quad[0] = FloatPoint(x1 + max(-adjbw1, 0), y1); |
+ quad[1] = FloatPoint(x1 + max(adjbw1, 0), y2); |
+ quad[2] = FloatPoint(x2 - max(adjbw2, 0), y2); |
+ quad[3] = FloatPoint(x2 - max(-adjbw2, 0), y1); |
+ break; |
+ case BSBottom: |
+ quad[0] = FloatPoint(x1 + max(adjbw1, 0), y1); |
+ quad[1] = FloatPoint(x1 + max(-adjbw1, 0), y2); |
+ quad[2] = FloatPoint(x2 - max(-adjbw2, 0), y2); |
+ quad[3] = FloatPoint(x2 - max(adjbw2, 0), y1); |
+ break; |
+ case BSLeft: |
+ quad[0] = FloatPoint(x1, y1 + max(-adjbw1, 0)); |
+ quad[1] = FloatPoint(x1, y2 - max(-adjbw2, 0)); |
+ quad[2] = FloatPoint(x2, y2 - max(adjbw2, 0)); |
+ quad[3] = FloatPoint(x2, y1 + max(adjbw1, 0)); |
+ break; |
+ case BSRight: |
+ quad[0] = FloatPoint(x1, y1 + max(adjbw1, 0)); |
+ quad[1] = FloatPoint(x1, y2 - max(adjbw2, 0)); |
+ quad[2] = FloatPoint(x2, y2 - max(-adjbw2, 0)); |
+ quad[3] = FloatPoint(x2, y1 + max(-adjbw1, 0)); |
+ break; |
+ } |
+ graphicsContext->drawConvexPolygon(4, quad); |
+ break; |
+ } |
+ } |
+} |
+ |
+bool RenderObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, |
+ const NinePieceImage& ninePieceImage, CompositeOperator op) |
+{ |
+ StyleImage* styleImage = ninePieceImage.image(); |
+ if (!styleImage || !styleImage->canRender(style->effectiveZoom())) |
+ return false; |
+ |
+ if (!styleImage->isLoaded()) |
+ return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. |
+ |
+ // If we have a border radius, the image gets clipped to the rounded rect. |
+ bool clipped = false; |
+ if (style->hasBorderRadius()) { |
+ IntRect clipRect(tx, ty, w, h); |
+ graphicsContext->save(); |
+ graphicsContext->addRoundedRectClip(clipRect, style->borderTopLeftRadius(), style->borderTopRightRadius(), |
+ style->borderBottomLeftRadius(), style->borderBottomRightRadius()); |
+ clipped = true; |
+ } |
+ |
+ // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function |
+ // doesn't have any understanding of the zoom that is in effect on the tile. |
+ styleImage->setImageContainerSize(IntSize(w, h)); |
+ IntSize imageSize = styleImage->imageSize(this, 1.0f); |
+ int imageWidth = imageSize.width(); |
+ int imageHeight = imageSize.height(); |
+ |
+ int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight)); |
+ int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight)); |
+ int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth)); |
+ int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth)); |
+ |
+ ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); |
+ ENinePieceImageRule vRule = ninePieceImage.verticalRule(); |
+ |
+ bool fitToBorder = style->borderImage() == ninePieceImage; |
+ |
+ int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; |
+ int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; |
+ int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; |
+ int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; |
+ |
+ bool drawLeft = leftSlice > 0 && leftWidth > 0; |
+ bool drawTop = topSlice > 0 && topWidth > 0; |
+ bool drawRight = rightSlice > 0 && rightWidth > 0; |
+ bool drawBottom = bottomSlice > 0 && bottomWidth > 0; |
+ bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && |
+ (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; |
+ |
+ Image* image = styleImage->image(this, imageSize); |
+ |
+ if (drawLeft) { |
+ // Paint the top and bottom left corners. |
+ |
+ // The top left corner rect is (tx, ty, leftWidth, topWidth) |
+ // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) |
+ if (drawTop) |
+ graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth), |
+ IntRect(0, 0, leftSlice, topSlice), op); |
+ |
+ // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) |
+ // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) |
+ if (drawBottom) |
+ graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), |
+ IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); |
+ |
+ // Paint the left edge. |
+ // Have to scale and tile into the border rect. |
+ graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth, |
+ h - topWidth - bottomWidth), |
+ IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), |
+ Image::StretchTile, (Image::TileRule)vRule, op); |
+ } |
+ |
+ if (drawRight) { |
+ // Paint the top and bottom right corners |
+ // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) |
+ // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) |
+ if (drawTop) |
+ graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), |
+ IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); |
+ |
+ // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) |
+ // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) |
+ if (drawBottom) |
+ graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), |
+ IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); |
+ |
+ // Paint the right edge. |
+ graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, |
+ h - topWidth - bottomWidth), |
+ IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), |
+ Image::StretchTile, (Image::TileRule)vRule, op); |
+ } |
+ |
+ // Paint the top edge. |
+ if (drawTop) |
+ graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), |
+ IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), |
+ (Image::TileRule)hRule, Image::StretchTile, op); |
+ |
+ // Paint the bottom edge. |
+ if (drawBottom) |
+ graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth, |
+ w - leftWidth - rightWidth, bottomWidth), |
+ IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), |
+ (Image::TileRule)hRule, Image::StretchTile, op); |
+ |
+ // Paint the middle. |
+ if (drawMiddle) |
+ graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, |
+ h - topWidth - bottomWidth), |
+ IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), |
+ (Image::TileRule)hRule, (Image::TileRule)vRule, op); |
+ |
+ // Clear the clip for the border radius. |
+ if (clipped) |
+ graphicsContext->restore(); |
+ |
+ return true; |
+} |
+ |
+void RenderObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, |
+ const RenderStyle* style, bool begin, bool end) |
+{ |
+ if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) |
+ return; |
+ |
+ const Color& tc = style->borderTopColor(); |
+ const Color& bc = style->borderBottomColor(); |
+ const Color& lc = style->borderLeftColor(); |
+ const Color& rc = style->borderRightColor(); |
+ |
+ bool tt = style->borderTopIsTransparent(); |
+ bool bt = style->borderBottomIsTransparent(); |
+ bool rt = style->borderRightIsTransparent(); |
+ bool lt = style->borderLeftIsTransparent(); |
+ |
+ EBorderStyle ts = style->borderTopStyle(); |
+ EBorderStyle bs = style->borderBottomStyle(); |
+ EBorderStyle ls = style->borderLeftStyle(); |
+ EBorderStyle rs = style->borderRightStyle(); |
+ |
+ bool renderTop = ts > BHIDDEN && !tt; |
+ bool renderLeft = ls > BHIDDEN && begin && !lt; |
+ bool renderRight = rs > BHIDDEN && end && !rt; |
+ bool renderBottom = bs > BHIDDEN && !bt; |
+ |
+ // Need sufficient width and height to contain border radius curves. Sanity check our border radii |
+ // and our width/height values to make sure the curves can all fit. If not, then we won't paint |
+ // any border radii. |
+ bool renderRadii = false; |
+ IntSize topLeft = style->borderTopLeftRadius(); |
+ IntSize topRight = style->borderTopRightRadius(); |
+ IntSize bottomLeft = style->borderBottomLeftRadius(); |
+ IntSize bottomRight = style->borderBottomRightRadius(); |
+ |
+ if (style->hasBorderRadius() && |
+ static_cast<unsigned>(w) >= static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()) && |
+ static_cast<unsigned>(w) >= static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width()) && |
+ static_cast<unsigned>(h) >= static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height()) && |
+ static_cast<unsigned>(h) >= static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height())) |
+ renderRadii = true; |
+ |
+ // Clip to the rounded rectangle. |
+ if (renderRadii) { |
+ graphicsContext->save(); |
+ graphicsContext->addRoundedRectClip(IntRect(tx, ty, w, h), topLeft, topRight, bottomLeft, bottomRight); |
+ } |
+ |
+ int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; |
+ float thickness; |
+ bool upperLeftBorderStylesMatch = renderLeft && (ts == ls) && (tc == lc); |
+ bool upperRightBorderStylesMatch = renderRight && (ts == rs) && (tc == rc) && (ts != OUTSET) && (ts != RIDGE) && (ts != INSET) && (ts != GROOVE); |
+ bool lowerLeftBorderStylesMatch = renderLeft && (bs == ls) && (bc == lc) && (bs != OUTSET) && (bs != RIDGE) && (bs != INSET) && (bs != GROOVE); |
+ bool lowerRightBorderStylesMatch = renderRight && (bs == rs) && (bc == rc); |
+ |
+ if (renderTop) { |
+ bool ignore_left = (renderRadii && topLeft.width() > 0) || |
+ (tc == lc && tt == lt && ts >= OUTSET && |
+ (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); |
+ |
+ bool ignore_right = (renderRadii && topRight.width() > 0) || |
+ (tc == rc && tt == rt && ts >= OUTSET && |
+ (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); |
+ |
+ int x = tx; |
+ int x2 = tx + w; |
+ if (renderRadii) { |
+ x += topLeft.width(); |
+ x2 -= topRight.width(); |
+ } |
+ |
+ drawBorder(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, |
+ ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); |
+ |
+ if (renderRadii) { |
+ int leftY = ty; |
+ |
+ // We make the arc double thick and let the clip rect take care of clipping the extra off. |
+ // We're doing this because it doesn't seem possible to match the curve of the clip exactly |
+ // with the arc-drawing function. |
+ thickness = style->borderTopWidth() * 2; |
+ |
+ if (topLeft.width()) { |
+ int leftX = tx; |
+ // The inner clip clips inside the arc. This is especially important for 1px borders. |
+ bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) |
+ && (style->borderTopWidth() < topLeft.height()) |
+ && (ts != DOUBLE || style->borderTopWidth() > 6); |
+ if (applyLeftInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), |
+ style->borderTopWidth()); |
+ } |
+ |
+ firstAngleStart = 90; |
+ firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; |
+ |
+ // Draw upper left arc |
+ drawBorderArc(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, |
+ BSTop, tc, style->color(), ts, true); |
+ if (applyLeftInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ |
+ if (topRight.width()) { |
+ int rightX = tx + w - topRight.width() * 2; |
+ bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) |
+ && (style->borderTopWidth() < topRight.height()) |
+ && (ts != DOUBLE || style->borderTopWidth() > 6); |
+ if (applyRightInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), |
+ style->borderTopWidth()); |
+ } |
+ |
+ if (upperRightBorderStylesMatch) { |
+ secondAngleStart = 0; |
+ secondAngleSpan = 90; |
+ } else { |
+ secondAngleStart = 45; |
+ secondAngleSpan = 45; |
+ } |
+ |
+ // Draw upper right arc |
+ drawBorderArc(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, |
+ BSTop, tc, style->color(), ts, false); |
+ if (applyRightInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ } |
+ } |
+ |
+ if (renderBottom) { |
+ bool ignore_left = (renderRadii && bottomLeft.width() > 0) || |
+ (bc == lc && bt == lt && bs >= OUTSET && |
+ (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); |
+ |
+ bool ignore_right = (renderRadii && bottomRight.width() > 0) || |
+ (bc == rc && bt == rt && bs >= OUTSET && |
+ (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); |
+ |
+ int x = tx; |
+ int x2 = tx + w; |
+ if (renderRadii) { |
+ x += bottomLeft.width(); |
+ x2 -= bottomRight.width(); |
+ } |
+ |
+ drawBorder(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bc, style->color(), bs, |
+ ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); |
+ |
+ if (renderRadii) { |
+ thickness = style->borderBottomWidth() * 2; |
+ |
+ if (bottomLeft.width()) { |
+ int leftX = tx; |
+ int leftY = ty + h - bottomLeft.height() * 2; |
+ bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) |
+ && (style->borderBottomWidth() < bottomLeft.height()) |
+ && (bs != DOUBLE || style->borderBottomWidth() > 6); |
+ if (applyLeftInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), |
+ style->borderBottomWidth()); |
+ } |
+ |
+ if (lowerLeftBorderStylesMatch) { |
+ firstAngleStart = 180; |
+ firstAngleSpan = 90; |
+ } else { |
+ firstAngleStart = 225; |
+ firstAngleSpan = 45; |
+ } |
+ |
+ // Draw lower left arc |
+ drawBorderArc(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, |
+ BSBottom, bc, style->color(), bs, true); |
+ if (applyLeftInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ |
+ if (bottomRight.width()) { |
+ int rightY = ty + h - bottomRight.height() * 2; |
+ int rightX = tx + w - bottomRight.width() * 2; |
+ bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) |
+ && (style->borderBottomWidth() < bottomRight.height()) |
+ && (bs != DOUBLE || style->borderBottomWidth() > 6); |
+ if (applyRightInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), |
+ style->borderBottomWidth()); |
+ } |
+ |
+ secondAngleStart = 270; |
+ secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; |
+ |
+ // Draw lower right arc |
+ drawBorderArc(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, |
+ BSBottom, bc, style->color(), bs, false); |
+ if (applyRightInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ } |
+ } |
+ |
+ if (renderLeft) { |
+ bool ignore_top = (renderRadii && topLeft.height() > 0) || |
+ (tc == lc && tt == lt && ls >= OUTSET && |
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); |
+ |
+ bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || |
+ (bc == lc && bt == lt && ls >= OUTSET && |
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); |
+ |
+ int y = ty; |
+ int y2 = ty + h; |
+ if (renderRadii) { |
+ y += topLeft.height(); |
+ y2 -= bottomLeft.height(); |
+ } |
+ |
+ drawBorder(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, |
+ ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); |
+ |
+ if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { |
+ int topX = tx; |
+ thickness = style->borderLeftWidth() * 2; |
+ |
+ if (!upperLeftBorderStylesMatch && topLeft.width()) { |
+ int topY = ty; |
+ bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) |
+ && (style->borderTopWidth() < topLeft.height()) |
+ && (ls != DOUBLE || style->borderLeftWidth() > 6); |
+ if (applyTopInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), |
+ style->borderLeftWidth()); |
+ } |
+ |
+ firstAngleStart = 135; |
+ firstAngleSpan = 45; |
+ |
+ // Draw top left arc |
+ drawBorderArc(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, |
+ BSLeft, lc, style->color(), ls, true); |
+ if (applyTopInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ |
+ if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { |
+ int bottomY = ty + h - bottomLeft.height() * 2; |
+ bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) |
+ && (style->borderBottomWidth() < bottomLeft.height()) |
+ && (ls != DOUBLE || style->borderLeftWidth() > 6); |
+ if (applyBottomInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), |
+ style->borderLeftWidth()); |
+ } |
+ |
+ secondAngleStart = 180; |
+ secondAngleSpan = 45; |
+ |
+ // Draw bottom left arc |
+ drawBorderArc(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, |
+ BSLeft, lc, style->color(), ls, false); |
+ if (applyBottomInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ } |
+ } |
+ |
+ if (renderRight) { |
+ bool ignore_top = (renderRadii && topRight.height() > 0) || |
+ ((tc == rc) && (tt == rt) && |
+ (rs >= DOTTED || rs == INSET) && |
+ (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); |
+ |
+ bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || |
+ ((bc == rc) && (bt == rt) && |
+ (rs >= DOTTED || rs == INSET) && |
+ (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); |
+ |
+ int y = ty; |
+ int y2 = ty + h; |
+ if (renderRadii) { |
+ y += topRight.height(); |
+ y2 -= bottomRight.height(); |
+ } |
+ |
+ drawBorder(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rc, style->color(), rs, |
+ ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); |
+ |
+ if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { |
+ thickness = style->borderRightWidth() * 2; |
+ |
+ if (!upperRightBorderStylesMatch && topRight.width()) { |
+ int topX = tx + w - topRight.width() * 2; |
+ int topY = ty; |
+ bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) |
+ && (style->borderTopWidth() < topRight.height()) |
+ && (rs != DOUBLE || style->borderRightWidth() > 6); |
+ if (applyTopInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), |
+ style->borderRightWidth()); |
+ } |
+ |
+ firstAngleStart = 0; |
+ firstAngleSpan = 45; |
+ |
+ // Draw top right arc |
+ drawBorderArc(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, |
+ BSRight, rc, style->color(), rs, true); |
+ if (applyTopInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ |
+ if (!lowerRightBorderStylesMatch && bottomRight.width()) { |
+ int bottomX = tx + w - bottomRight.width() * 2; |
+ int bottomY = ty + h - bottomRight.height() * 2; |
+ bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) |
+ && (style->borderBottomWidth() < bottomRight.height()) |
+ && (rs != DOUBLE || style->borderRightWidth() > 6); |
+ if (applyBottomInnerClip) { |
+ graphicsContext->save(); |
+ graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), |
+ style->borderRightWidth()); |
+ } |
+ |
+ secondAngleStart = 315; |
+ secondAngleSpan = 45; |
+ |
+ // Draw bottom right arc |
+ drawBorderArc(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, |
+ BSRight, rc, style->color(), rs, false); |
+ if (applyBottomInnerClip) |
+ graphicsContext->restore(); |
+ } |
+ } |
+ } |
+ |
+ if (renderRadii) |
+ graphicsContext->restore(); |
+} |
+ |
+void RenderObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, bool begin, bool end) |
+{ |
+ // FIXME: Deal with border-image. Would be great to use border-image as a mask. |
+ |
+ IntRect rect(tx, ty, w, h); |
+ bool hasBorderRadius = s->hasBorderRadius(); |
+ bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255; |
+ for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) { |
+ context->save(); |
+ |
+ IntSize shadowOffset(shadow->x, shadow->y); |
+ int shadowBlur = shadow->blur; |
+ IntRect fillRect(rect); |
+ |
+ if (hasBorderRadius) { |
+ IntRect shadowRect(rect); |
+ shadowRect.inflate(shadowBlur); |
+ shadowRect.move(shadowOffset); |
+ context->clip(shadowRect); |
+ |
+ // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not |
+ // bleed in (due to antialiasing) if the context is transformed. |
+ IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 1, 0); |
+ shadowOffset -= extraOffset; |
+ fillRect.move(extraOffset); |
+ } |
+ |
+ context->setShadow(shadowOffset, shadowBlur, shadow->color); |
+ if (hasBorderRadius) { |
+ IntSize topLeft = begin ? s->borderTopLeftRadius() : IntSize(); |
+ IntSize topRight = end ? s->borderTopRightRadius() : IntSize(); |
+ IntSize bottomLeft = begin ? s->borderBottomLeftRadius() : IntSize(); |
+ IntSize bottomRight = end ? s->borderBottomRightRadius() : IntSize(); |
+ if (!hasOpaqueBackground) |
+ context->clipOutRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); |
+ context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); |
+ } else { |
+ if (!hasOpaqueBackground) |
+ context->clipOut(rect); |
+ context->fillRect(fillRect, Color::black); |
+ } |
+ context->restore(); |
+ } |
+} |
+ |
+void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect) |
+{ |
+ if (rect.isEmpty()) |
+ return; |
+ Node* node = element(); |
+ if (!node || !node->isLink() || !node->isElementNode()) |
+ return; |
+ const AtomicString& href = static_cast<Element*>(node)->getAttribute(hrefAttr); |
+ if (href.isNull()) |
+ return; |
+ context->setURLForRect(node->document()->completeURL(href), rect); |
+} |
+ |
+void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style) |
+{ |
+ if (!hasOutline()) |
+ return; |
+ |
+ int ow = style->outlineWidth(); |
+ EBorderStyle os = style->outlineStyle(); |
+ |
+ Color oc = style->outlineColor(); |
+ if (!oc.isValid()) |
+ oc = style->color(); |
+ |
+ int offset = style->outlineOffset(); |
+ |
+ if (style->outlineStyleIsAuto() || hasOutlineAnnotation()) { |
+ if (!theme()->supportsFocusRing(style)) { |
+ // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. |
+ graphicsContext->initFocusRing(ow, offset); |
+ addFocusRingRects(graphicsContext, tx, ty); |
+ if (style->outlineStyleIsAuto()) |
+ graphicsContext->drawFocusRing(oc); |
+ else |
+ addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); |
+ graphicsContext->clearFocusRing(); |
+ } |
+ } |
+ |
+ if (style->outlineStyleIsAuto() || style->outlineStyle() == BNONE) |
+ return; |
+ |
+ tx -= offset; |
+ ty -= offset; |
+ w += 2 * offset; |
+ h += 2 * offset; |
+ |
+ if (h < 0 || w < 0) |
+ return; |
+ |
+ drawBorder(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, |
+ BSLeft, Color(oc), style->color(), os, ow, ow); |
+ |
+ drawBorder(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, |
+ BSTop, Color(oc), style->color(), os, ow, ow); |
+ |
+ drawBorder(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, |
+ BSRight, Color(oc), style->color(), os, ow, ow); |
+ |
+ drawBorder(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, |
+ BSBottom, Color(oc), style->color(), os, ow, ow); |
+} |
+ |
+ |
+void RenderObject::absoluteRectsForRange(Vector<IntRect>& rects, unsigned start, unsigned end, bool) |
+{ |
+ if (!virtualChildren()->firstChild()) { |
+ if ((isInline() || isAnonymousBlock())) { |
+ FloatPoint absPos = localToAbsolute(FloatPoint()); |
+ absoluteRects(rects, absPos.x(), absPos.y()); |
+ } |
+ return; |
+ } |
+ |
+ unsigned offset = start; |
+ for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { |
+ if (child->isText() || child->isInline() || child->isAnonymousBlock()) { |
+ FloatPoint absPos = child->localToAbsolute(FloatPoint()); |
+ child->absoluteRects(rects, absPos.x(), absPos.y()); |
+ } |
+ } |
+} |
+ |
+void RenderObject::absoluteQuadsForRange(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool) |
+{ |
+ if (!virtualChildren()->firstChild()) { |
+ if (isInline() || isAnonymousBlock()) |
+ absoluteQuads(quads); |
+ return; |
+ } |
+ |
+ unsigned offset = start; |
+ for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { |
+ if (child->isText() || child->isInline() || child->isAnonymousBlock()) |
+ child->absoluteQuads(quads); |
+ } |
+} |
+ |
+IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) |
+{ |
+ if (useTransforms) { |
+ Vector<FloatQuad> quads; |
+ absoluteQuads(quads); |
+ |
+ size_t n = quads.size(); |
+ if (!n) |
+ return IntRect(); |
+ |
+ IntRect result = quads[0].enclosingBoundingBox(); |
+ for (size_t i = 1; i < n; ++i) |
+ result.unite(quads[i].enclosingBoundingBox()); |
+ return result; |
+ } |
+ |
+ FloatPoint absPos = localToAbsolute(); |
+ Vector<IntRect> rects; |
+ absoluteRects(rects, absPos.x(), absPos.y()); |
+ |
+ size_t n = rects.size(); |
+ if (!n) |
+ return IntRect(); |
+ |
+ IntRect result = rects[0]; |
+ for (size_t i = 1; i < n; ++i) |
+ result.unite(rects[i]); |
+ return result; |
+} |
+ |
+void RenderObject::addAbsoluteRectForLayer(IntRect& result) |
+{ |
+ if (hasLayer()) |
+ result.unite(absoluteBoundingBoxRect()); |
+ for (RenderObject* current = firstChild(); current; current = current->nextSibling()) |
+ current->addAbsoluteRectForLayer(result); |
+} |
+ |
+IntRect RenderObject::paintingRootRect(IntRect& topLevelRect) |
+{ |
+ IntRect result = absoluteBoundingBoxRect(); |
+ topLevelRect = result; |
+ for (RenderObject* current = firstChild(); current; current = current->nextSibling()) |
+ current->addAbsoluteRectForLayer(result); |
+ return result; |
+} |
+ |
+void RenderObject::paint(PaintInfo& /*paintInfo*/, int /*tx*/, int /*ty*/) |
+{ |
+} |
+ |
+RenderBox* RenderObject::containerForRepaint() const |
+{ |
+#if USE(ACCELERATED_COMPOSITING) |
+ if (RenderView* v = view()) { |
+ if (v->usesCompositing()) { |
+ RenderLayer* compLayer = enclosingCompositingLayer(); |
+ return compLayer ? compLayer->renderer() : 0; |
+ } |
+ } |
+#endif |
+ // Do root-relative repaint. |
+ return 0; |
+} |
+ |
+void RenderObject::repaintUsingContainer(RenderBox* repaintContainer, const IntRect& r, bool immediate) |
+{ |
+ if (!repaintContainer || repaintContainer->isRenderView()) { |
+ RenderView* v = repaintContainer ? toRenderView(repaintContainer) : view(); |
+ v->repaintViewRectangle(r, immediate); |
+ } else { |
+#if USE(ACCELERATED_COMPOSITING) |
+ RenderView* v = view(); |
+ if (v->usesCompositing()) { |
+ ASSERT(repaintContainer->hasLayer() && repaintContainer->layer()->isComposited()); |
+ repaintContainer->layer()->setBackingNeedsRepaintInRect(r); |
+ } |
+#else |
+ ASSERT_NOT_REACHED(); |
+#endif |
+ } |
+} |
+ |
+void RenderObject::repaint(bool immediate) |
+{ |
+ // Can't use view(), since we might be unrooted. |
+ RenderObject* o = this; |
+ while (o->parent()) |
+ o = o->parent(); |
+ if (!o->isRenderView()) |
+ return; |
+ |
+ RenderView* view = toRenderView(o); |
+ if (view->printing()) |
+ return; // Don't repaint if we're printing. |
+ |
+ RenderBox* repaintContainer = containerForRepaint(); |
+ repaintUsingContainer(repaintContainer ? repaintContainer : view, clippedOverflowRectForRepaint(repaintContainer), immediate); |
+} |
+ |
+void RenderObject::repaintRectangle(const IntRect& r, bool immediate) |
+{ |
+ // Can't use view(), since we might be unrooted. |
+ RenderObject* o = this; |
+ while (o->parent()) |
+ o = o->parent(); |
+ if (!o->isRenderView()) |
+ return; |
+ |
+ RenderView* view = toRenderView(o); |
+ if (view->printing()) |
+ return; // Don't repaint if we're printing. |
+ |
+ IntRect dirtyRect(r); |
+ |
+ // FIXME: layoutDelta needs to be applied in parts before/after transforms and |
+ // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 |
+ dirtyRect.move(view->layoutDelta()); |
+ |
+ RenderBox* repaintContainer = containerForRepaint(); |
+ computeRectForRepaint(repaintContainer, dirtyRect); |
+ repaintUsingContainer(repaintContainer ? repaintContainer : view, dirtyRect, immediate); |
+} |
+ |
+bool RenderObject::repaintAfterLayoutIfNeeded(RenderBox* repaintContainer, const IntRect& oldBounds, const IntRect& oldOutlineBox) |
+{ |
+ RenderView* v = view(); |
+ if (v->printing()) |
+ return false; // Don't repaint if we're printing. |
+ |
+ IntRect newBounds = clippedOverflowRectForRepaint(repaintContainer); |
+ IntRect newOutlineBox; |
+ |
+ bool fullRepaint = selfNeedsLayout(); |
+ // Presumably a background or a border exists if border-fit:lines was specified. |
+ if (!fullRepaint && style()->borderFit() == BorderFitLines) |
+ fullRepaint = true; |
+ if (!fullRepaint) { |
+ newOutlineBox = outlineBoundsForRepaint(repaintContainer); |
+ if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) |
+ fullRepaint = true; |
+ } |
+ |
+ if (!repaintContainer) |
+ repaintContainer = v; |
+ |
+ if (fullRepaint) { |
+ repaintUsingContainer(repaintContainer, oldBounds); |
+ if (newBounds != oldBounds) |
+ repaintUsingContainer(repaintContainer, newBounds); |
+ return true; |
+ } |
+ |
+ if (newBounds == oldBounds && newOutlineBox == oldOutlineBox) |
+ return false; |
+ |
+ int deltaLeft = newBounds.x() - oldBounds.x(); |
+ if (deltaLeft > 0) |
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); |
+ else if (deltaLeft < 0) |
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); |
+ |
+ int deltaRight = newBounds.right() - oldBounds.right(); |
+ if (deltaRight > 0) |
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height())); |
+ else if (deltaRight < 0) |
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height())); |
+ |
+ int deltaTop = newBounds.y() - oldBounds.y(); |
+ if (deltaTop > 0) |
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); |
+ else if (deltaTop < 0) |
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); |
+ |
+ int deltaBottom = newBounds.bottom() - oldBounds.bottom(); |
+ if (deltaBottom > 0) |
+ repaintUsingContainer(repaintContainer, IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom)); |
+ else if (deltaBottom < 0) |
+ repaintUsingContainer(repaintContainer, IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom)); |
+ |
+ if (newOutlineBox == oldOutlineBox) |
+ return false; |
+ |
+ // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly |
+ // two rectangles (but typically only one). |
+ RenderStyle* outlineStyle = outlineStyleForRepaint(); |
+ int ow = outlineStyle->outlineSize(); |
+ ShadowData* boxShadow = style()->boxShadow(); |
+ int width = abs(newOutlineBox.width() - oldOutlineBox.width()); |
+ if (width) { |
+ int shadowRight = 0; |
+ for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) |
+ shadowRight = max(shadow->x + shadow->blur, shadowRight); |
+ |
+ int borderRight = isBox() ? toRenderBox(this)->borderRight() : 0; |
+ int borderWidth = max(-outlineStyle->outlineOffset(), max(borderRight, max(style()->borderTopRightRadius().width(), style()->borderBottomRightRadius().width()))) + max(ow, shadowRight); |
+ IntRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - borderWidth, |
+ newOutlineBox.y(), |
+ width + borderWidth, |
+ max(newOutlineBox.height(), oldOutlineBox.height())); |
+ int right = min(newBounds.right(), oldBounds.right()); |
+ if (rightRect.x() < right) { |
+ rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); |
+ repaintUsingContainer(repaintContainer, rightRect); |
+ } |
+ } |
+ int height = abs(newOutlineBox.height() - oldOutlineBox.height()); |
+ if (height) { |
+ int shadowBottom = 0; |
+ for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) |
+ shadowBottom = max(shadow->y + shadow->blur, shadowBottom); |
+ |
+ int borderBottom = isBox() ? toRenderBox(this)->borderBottom() : 0; |
+ int borderHeight = max(-outlineStyle->outlineOffset(), max(borderBottom, max(style()->borderBottomLeftRadius().height(), style()->borderBottomRightRadius().height()))) + max(ow, shadowBottom); |
+ IntRect bottomRect(newOutlineBox.x(), |
+ min(newOutlineBox.bottom(), oldOutlineBox.bottom()) - borderHeight, |
+ max(newOutlineBox.width(), oldOutlineBox.width()), |
+ height + borderHeight); |
+ int bottom = min(newBounds.bottom(), oldBounds.bottom()); |
+ if (bottomRect.y() < bottom) { |
+ bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); |
+ repaintUsingContainer(repaintContainer, bottomRect); |
+ } |
+ } |
+ return false; |
+} |
+ |
+void RenderObject::repaintDuringLayoutIfMoved(const IntRect&) |
+{ |
+} |
+ |
+void RenderObject::repaintOverhangingFloats(bool) |
+{ |
+} |
+ |
+bool RenderObject::checkForRepaintDuringLayout() const |
+{ |
+ // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=20885> It is probably safe to also require |
+ // m_everHadLayout. Currently, only RenderBlock::layoutBlock() adds this condition. See also |
+ // <https://bugs.webkit.org/show_bug.cgi?id=15129>. |
+ return !document()->view()->needsFullRepaint() && !hasLayer(); |
+} |
+ |
+IntRect RenderObject::rectWithOutlineForRepaint(RenderBox* repaintContainer, int outlineWidth) |
+{ |
+ IntRect r(clippedOverflowRectForRepaint(repaintContainer)); |
+ r.inflate(outlineWidth); |
+ return r; |
+} |
+ |
+IntRect RenderObject::clippedOverflowRectForRepaint(RenderBox* repaintContainer) |
+{ |
+ if (parent()) |
+ return parent()->clippedOverflowRectForRepaint(repaintContainer); |
+ return IntRect(); |
+} |
+ |
+void RenderObject::computeRectForRepaint(RenderBox* repaintContainer, IntRect& rect, bool fixed) |
+{ |
+ if (repaintContainer == this) |
+ return; |
+ |
+ if (RenderObject* o = parent()) { |
+ if (o->isBlockFlow()) { |
+ RenderBlock* cb = toRenderBlock(o); |
+ if (cb->hasColumns()) |
+ cb->adjustRectForColumns(rect); |
+ } |
+ |
+ if (o->hasOverflowClip()) { |
+ // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the |
+ // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint |
+ // anyway if its size does change. |
+ RenderBox* boxParent = toRenderBox(o); |
+ |
+ IntRect boxRect(0, 0, boxParent->layer()->width(), boxParent->layer()->height()); |
+ int x = rect.x(); |
+ int y = rect.y(); |
+ boxParent->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden. |
+ IntRect repaintRect(x, y, rect.width(), rect.height()); |
+ rect = intersection(repaintRect, boxRect); |
+ if (rect.isEmpty()) |
+ return; |
+ } |
+ |
+ o->computeRectForRepaint(repaintContainer, rect, fixed); |
+ } |
+} |
+ |
+void RenderObject::dirtyLinesFromChangedChild(RenderObject*) |
+{ |
+} |
+ |
+#ifndef NDEBUG |
+ |
+void RenderObject::showTreeForThis() const |
+{ |
+ if (element()) |
+ element()->showTreeForThis(); |
+} |
+ |
+#endif // NDEBUG |
+ |
+Color RenderObject::selectionBackgroundColor() const |
+{ |
+ Color color; |
+ if (style()->userSelect() != SELECT_NONE) { |
+ RenderStyle* pseudoStyle = getCachedPseudoStyle(SELECTION); |
+ if (pseudoStyle && pseudoStyle->backgroundColor().isValid()) |
+ color = pseudoStyle->backgroundColor().blendWithWhite(); |
+ else |
+ color = document()->frame()->selection()->isFocusedAndActive() ? |
+ theme()->activeSelectionBackgroundColor() : |
+ theme()->inactiveSelectionBackgroundColor(); |
+ } |
+ |
+ return color; |
+} |
+ |
+Color RenderObject::selectionForegroundColor() const |
+{ |
+ Color color; |
+ if (style()->userSelect() == SELECT_NONE) |
+ return color; |
+ |
+ if (RenderStyle* pseudoStyle = getCachedPseudoStyle(SELECTION)) { |
+ color = pseudoStyle->textFillColor(); |
+ if (!color.isValid()) |
+ color = pseudoStyle->color(); |
+ } else |
+ color = document()->frame()->selection()->isFocusedAndActive() ? |
+ theme()->activeSelectionForegroundColor() : |
+ theme()->inactiveSelectionForegroundColor(); |
+ |
+ return color; |
+} |
+ |
+Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const |
+{ |
+ if (!dhtmlOK && !uaOK) |
+ return 0; |
+ |
+ for (const RenderObject* curr = this; curr; curr = curr->parent()) { |
+ Node* elt = curr->element(); |
+ if (elt && elt->nodeType() == Node::TEXT_NODE) { |
+ // Since there's no way for the author to address the -webkit-user-drag style for a text node, |
+ // we use our own judgement. |
+ if (uaOK && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { |
+ dhtmlWillDrag = false; |
+ return curr->node(); |
+ } |
+ if (elt->canStartSelection()) |
+ // In this case we have a click in the unselected portion of text. If this text is |
+ // selectable, we want to start the selection process instead of looking for a parent |
+ // to try to drag. |
+ return 0; |
+ } else { |
+ EUserDrag dragMode = curr->style()->userDrag(); |
+ if (dhtmlOK && dragMode == DRAG_ELEMENT) { |
+ dhtmlWillDrag = true; |
+ return curr->node(); |
+ } |
+ if (uaOK && dragMode == DRAG_AUTO |
+ && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { |
+ dhtmlWillDrag = false; |
+ return curr->node(); |
+ } |
+ } |
+ } |
+ return 0; |
+} |
+ |
+void RenderObject::selectionStartEnd(int& spos, int& epos) const |
+{ |
+ view()->selectionStartEnd(spos, epos); |
+} |
+ |
+RenderBlock* RenderObject::createAnonymousBlock() |
+{ |
+ RefPtr<RenderStyle> newStyle = RenderStyle::create(); |
+ newStyle->inheritFrom(m_style.get()); |
+ newStyle->setDisplay(BLOCK); |
+ |
+ RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); |
+ newBox->setStyle(newStyle.release()); |
+ return newBox; |
+} |
+ |
+void RenderObject::handleDynamicFloatPositionChange() |
+{ |
+ // We have gone from not affecting the inline status of the parent flow to suddenly |
+ // having an impact. See if there is a mismatch between the parent flow's |
+ // childrenInline() state and our state. |
+ setInline(style()->isDisplayInlineType()); |
+ if (isInline() != parent()->childrenInline()) { |
+ if (!isInline()) |
+ toRenderBox(parent())->childBecameNonInline(this); |
+ else { |
+ // An anonymous block must be made to wrap this inline. |
+ RenderBlock* block = createAnonymousBlock(); |
+ RenderObjectChildList* childlist = parent()->virtualChildren(); |
+ childlist->insertChildNode(parent(), block, this); |
+ block->children()->appendChildNode(block, childlist->removeChildNode(parent(), this)); |
+ } |
+ } |
+} |
+ |
+void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) |
+{ |
+ if (!isText() && style) |
+ setStyle(animation()->updateAnimations(this, style.get())); |
+ else |
+ setStyle(style); |
+} |
+ |
+void RenderObject::setStyle(PassRefPtr<RenderStyle> style) |
+{ |
+ if (m_style == style) |
+ return; |
+ |
+ StyleDifference diff = StyleDifferenceEqual; |
+ if (m_style) |
+ diff = m_style->diff(style.get()); |
+ |
+ // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. |
+ if (diff == StyleDifferenceRepaintLayer && !hasLayer()) |
+ diff = StyleDifferenceRepaint; |
+ |
+ styleWillChange(diff, style.get()); |
+ |
+ RefPtr<RenderStyle> oldStyle = m_style.release(); |
+ m_style = style; |
+ |
+ updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0); |
+ updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0); |
+ |
+ updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); |
+ updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); |
+ |
+ styleDidChange(diff, oldStyle.get()); |
+} |
+ |
+void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style) |
+{ |
+ m_style = style; |
+} |
+ |
+void RenderObject::styleWillChange(StyleDifference diff, const RenderStyle* newStyle) |
+{ |
+ if (m_style) { |
+ // If our z-index changes value or our visibility changes, |
+ // we need to dirty our stacking context's z-order list. |
+ if (newStyle) { |
+#if ENABLE(DASHBOARD_SUPPORT) |
+ if (m_style->visibility() != newStyle->visibility() || |
+ m_style->zIndex() != newStyle->zIndex() || |
+ m_style->hasAutoZIndex() != newStyle->hasAutoZIndex()) |
+ document()->setDashboardRegionsDirty(true); |
+#endif |
+ |
+ // Keep layer hierarchy visibility bits up to date if visibility changes. |
+ if (m_style->visibility() != newStyle->visibility()) { |
+ if (RenderLayer* l = enclosingLayer()) { |
+ if (newStyle->visibility() == VISIBLE) |
+ l->setHasVisibleContent(true); |
+ else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { |
+ l->dirtyVisibleContentStatus(); |
+ if (diff > StyleDifferenceRepaintLayer) |
+ repaint(); |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (m_parent && (diff == StyleDifferenceRepaint || newStyle->outlineSize() < m_style->outlineSize())) |
+ repaint(); |
+ if (isFloating() && (m_style->floating() != newStyle->floating())) |
+ // For changes in float styles, we need to conceivably remove ourselves |
+ // from the floating objects list. |
+ toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); |
+ else if (isPositioned() && (newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)) |
+ // For changes in positioning styles, we need to conceivably remove ourselves |
+ // from the positioned objects list. |
+ toRenderBox(this)->removeFloatingOrPositionedChildFromBlockLists(); |
+ |
+ s_affectsParentBlock = isFloatingOrPositioned() && |
+ (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition) |
+ && parent() && (parent()->isBlockFlow() || parent()->isRenderInline()); |
+ |
+ // reset style flags |
+ if (diff == StyleDifferenceLayout || diff == StyleDifferenceLayoutPositionedMovementOnly) { |
+ m_floating = false; |
+ m_positioned = false; |
+ m_relPositioned = false; |
+ } |
+ m_paintBackground = false; |
+ m_hasOverflowClip = false; |
+ m_hasTransform = false; |
+ m_hasReflection = false; |
+ } else |
+ s_affectsParentBlock = false; |
+ |
+ if (view()->frameView()) { |
+ // FIXME: A better solution would be to only invalidate the fixed regions when scrolling. It's overkill to |
+ // prevent the entire view from blitting on a scroll. |
+ bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition || newStyle->hasFixedBackgroundImage()); |
+ bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition || m_style->hasFixedBackgroundImage()); |
+ if (oldStyleSlowScroll != newStyleSlowScroll) { |
+ if (oldStyleSlowScroll) |
+ view()->frameView()->removeSlowRepaintObject(); |
+ if (newStyleSlowScroll) |
+ view()->frameView()->addSlowRepaintObject(); |
+ } |
+ } |
+} |
+ |
+void RenderObject::styleDidChange(StyleDifference diff, const RenderStyle*) |
+{ |
+ setHasBoxDecorations(m_style->hasBorder() || m_style->hasBackground() || m_style->hasAppearance() || m_style->boxShadow()); |
+ |
+ if (s_affectsParentBlock) |
+ handleDynamicFloatPositionChange(); |
+ |
+ if (!m_parent) |
+ return; |
+ |
+ if (diff == StyleDifferenceLayout) |
+ setNeedsLayoutAndPrefWidthsRecalc(); |
+ else if (diff == StyleDifferenceLayoutPositionedMovementOnly) |
+ setNeedsPositionedMovementLayout(); |
+ else if (diff == StyleDifferenceRepaintLayer || diff == StyleDifferenceRepaint) |
+ // Do a repaint with the new style now, e.g., for example if we go from |
+ // not having an outline to having an outline. |
+ repaint(); |
+} |
+ |
+void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) |
+{ |
+ // FIXME: This will be slow when a large number of images is used. Fix by using a dict. |
+ for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { |
+ if (currOld->image() && (!newLayers || !newLayers->containsImage(currOld->image()))) |
+ currOld->image()->removeClient(this); |
+ } |
+ for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { |
+ if (currNew->image() && (!oldLayers || !oldLayers->containsImage(currNew->image()))) |
+ currNew->image()->addClient(this); |
+ } |
+} |
+ |
+void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) |
+{ |
+ if (oldImage != newImage) { |
+ if (oldImage) |
+ oldImage->removeClient(this); |
+ if (newImage) |
+ newImage->addClient(this); |
+ } |
+} |
+ |
+IntRect RenderObject::viewRect() const |
+{ |
+ return view()->viewRect(); |
+} |
+ |
+FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const |
+{ |
+ RenderObject* o = parent(); |
+ if (o) { |
+ if (o->hasOverflowClip()) |
+ localPoint -= toRenderBox(o)->layer()->scrolledContentOffset(); |
+ return o->localToAbsolute(localPoint, fixed, useTransforms); |
+ } |
+ |
+ return FloatPoint(); |
+} |
+ |
+FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const |
+{ |
+ RenderObject* o = parent(); |
+ if (o) { |
+ FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); |
+ if (o->hasOverflowClip()) |
+ localPoint += toRenderBox(o)->layer()->scrolledContentOffset(); |
+ return localPoint; |
+ } |
+ return FloatPoint(); |
+} |
+ |
+FloatQuad RenderObject::localToContainerQuad(const FloatQuad& localQuad, RenderBox* repaintContainer, bool fixed) const |
+{ |
+ if (repaintContainer == this) |
+ return localQuad; |
+ |
+ RenderObject* o = parent(); |
+ if (o) { |
+ FloatQuad quad = localQuad; |
+ if (o->hasOverflowClip()) |
+ quad -= toRenderBox(o)->layer()->scrolledContentOffset(); |
+ return o->localToContainerQuad(quad, repaintContainer, fixed); |
+ } |
+ |
+ return FloatQuad(); |
+} |
+ |
+IntSize RenderObject::offsetFromContainer(RenderObject* o) const |
+{ |
+ ASSERT(o == container()); |
+ |
+ IntSize offset; |
+ if (o->hasOverflowClip()) |
+ offset -= toRenderBox(o)->layer()->scrolledContentOffset(); |
+ |
+ return offset; |
+} |
+ |
+IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine) |
+{ |
+ if (extraWidthToEndOfLine) |
+ *extraWidthToEndOfLine = 0; |
+ |
+ return IntRect(); |
+} |
+ |
+RenderView* RenderObject::view() const |
+{ |
+ return toRenderView(document()->renderer()); |
+} |
+ |
+bool RenderObject::hasOutlineAnnotation() const |
+{ |
+ return element() && element()->isLink() && document()->printing(); |
+} |
+ |
+RenderObject* RenderObject::container() const |
+{ |
+ // This method is extremely similar to containingBlock(), but with a few notable |
+ // exceptions. |
+ // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when |
+ // the object is not part of the primary document subtree yet. |
+ // (2) For normal flow elements, it just returns the parent. |
+ // (3) For absolute positioned elements, it will return a relative positioned inline. |
+ // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle |
+ // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and |
+ // calcAbsoluteVertical have to use container(). |
+ RenderObject* o = parent(); |
+ |
+ if (isText()) |
+ return o; |
+ |
+ EPosition pos = m_style->position(); |
+ if (pos == FixedPosition) { |
+ // container() can be called on an object that is not in the |
+ // tree yet. We don't call view() since it will assert if it |
+ // can't get back to the canvas. Instead we just walk as high up |
+ // as we can. If we're in the tree, we'll get the root. If we |
+ // aren't we'll get the root of our little subtree (most likely |
+ // we'll just return 0). |
+ // FIXME: The definition of view() has changed to not crawl up the render tree. It might |
+ // be safe now to use it. |
+ while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) |
+ o = o->parent(); |
+ } else if (pos == AbsolutePosition) { |
+ // Same goes here. We technically just want our containing block, but |
+ // we may not have one if we're part of an uninstalled subtree. We'll |
+ // climb as high as we can though. |
+ while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) |
+ o = o->parent(); |
+ } |
+ |
+ return o; |
+} |
+ |
+bool RenderObject::isSelectionBorder() const |
+{ |
+ SelectionState st = selectionState(); |
+ return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; |
+} |
+ |
+void RenderObject::destroy() |
+{ |
+ // Destroy any leftover anonymous children. |
+ RenderObjectChildList* children = virtualChildren(); |
+ if (children) |
+ children->destroyLeftoverChildren(); |
+ |
+ // If this renderer is being autoscrolled, stop the autoscroll timer |
+ if (document()->frame() && document()->frame()->eventHandler()->autoscrollRenderer() == this) |
+ document()->frame()->eventHandler()->stopAutoscrollTimer(true); |
+ |
+ if (m_hasCounterNodeMap) |
+ RenderCounter::destroyCounterNodes(this); |
+ |
+ if (AXObjectCache::accessibilityEnabled()) { |
+ document()->axObjectCache()->childrenChanged(this->parent()); |
+ document()->axObjectCache()->remove(this); |
+ } |
+ animation()->cancelAnimations(this); |
+ |
+ // By default no ref-counting. RenderWidget::destroy() doesn't call |
+ // this function because it needs to do ref-counting. If anything |
+ // in this function changes, be sure to fix RenderWidget::destroy() as well. |
+ |
+ remove(); |
+ |
+ // FIXME: Would like to do this in RenderBox, but the timing is so complicated that this can't easily |
+ // be moved into RenderBox::destroy. |
+ RenderArena* arena = renderArena(); |
+ if (hasLayer()) |
+ toRenderBox(this)->layer()->destroy(arena); |
+ arenaDelete(arena, this); |
+} |
+ |
+void RenderObject::arenaDelete(RenderArena* arena, void* base) |
+{ |
+ if (m_style) { |
+ for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { |
+ if (StyleImage* backgroundImage = bgLayer->image()) |
+ backgroundImage->removeClient(this); |
+ } |
+ |
+ for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { |
+ if (StyleImage* maskImage = maskLayer->image()) |
+ maskImage->removeClient(this); |
+ } |
+ |
+ if (StyleImage* borderImage = m_style->borderImage().image()) |
+ borderImage->removeClient(this); |
+ |
+ if (StyleImage* maskBoxImage = m_style->maskBoxImage().image()) |
+ maskBoxImage->removeClient(this); |
+ } |
+ |
+#ifndef NDEBUG |
+ void* savedBase = baseOfRenderObjectBeingDeleted; |
+ baseOfRenderObjectBeingDeleted = base; |
+#endif |
+ delete this; |
+#ifndef NDEBUG |
+ baseOfRenderObjectBeingDeleted = savedBase; |
+#endif |
+ |
+ // Recover the size left there for us by operator delete and free the memory. |
+ arena->free(*(size_t*)base, base); |
+} |
+ |
+VisiblePosition RenderObject::positionForCoordinates(int, int) |
+{ |
+ return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); |
+} |
+ |
+VisiblePosition RenderObject::positionForPoint(const IntPoint& point) |
+{ |
+ return positionForCoordinates(point.x(), point.y()); |
+} |
+ |
+void RenderObject::updateDragState(bool dragOn) |
+{ |
+ bool valueChanged = (dragOn != m_isDragging); |
+ m_isDragging = dragOn; |
+ if (valueChanged && style()->affectedByDragRules()) |
+ element()->setChanged(); |
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
+ curr->updateDragState(dragOn); |
+} |
+ |
+bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter) |
+{ |
+ bool inside = false; |
+ if (hitTestFilter != HitTestSelf) { |
+ // First test the foreground layer (lines and inlines). |
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestForeground); |
+ |
+ // Test floats next. |
+ if (!inside) |
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestFloat); |
+ |
+ // Finally test to see if the mouse is in the background (within a child block's background). |
+ if (!inside) |
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestChildBlockBackgrounds); |
+ } |
+ |
+ // See if the mouse is inside us but not any of our descendants |
+ if (hitTestFilter != HitTestDescendants && !inside) |
+ inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestBlockBackground); |
+ |
+ return inside; |
+} |
+ |
+void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& point) |
+{ |
+ if (result.innerNode()) |
+ return; |
+ |
+ Node* node = element(); |
+ if (node) { |
+ result.setInnerNode(node); |
+ if (!result.innerNonSharedNode()) |
+ result.setInnerNonSharedNode(node); |
+ result.setLocalPoint(point); |
+ } |
+} |
+ |
+bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, int /*y*/, int /*tx*/, int /*ty*/, HitTestAction) |
+{ |
+ return false; |
+} |
+ |
+int RenderObject::verticalPositionHint(bool firstLine) const |
+{ |
+ if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. |
+ firstLine = document()->usesFirstLineRules(); |
+ int vpos = m_verticalPosition; |
+ if (m_verticalPosition == PositionUndefined || firstLine) { |
+ vpos = getVerticalPosition(firstLine); |
+ if (!firstLine) |
+ m_verticalPosition = vpos; |
+ } |
+ |
+ return vpos; |
+} |
+ |
+int RenderObject::getVerticalPosition(bool firstLine) const |
+{ |
+ if (!isInline()) |
+ return 0; |
+ |
+ // This method determines the vertical position for inline elements. |
+ int vpos = 0; |
+ EVerticalAlign va = style()->verticalAlign(); |
+ if (va == TOP) |
+ vpos = PositionTop; |
+ else if (va == BOTTOM) |
+ vpos = PositionBottom; |
+ else { |
+ bool checkParent = parent()->isInline() && !parent()->isInlineBlockOrInlineTable() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM; |
+ vpos = checkParent ? parent()->verticalPositionHint(firstLine) : 0; |
+ // don't allow elements nested inside text-top to have a different valignment. |
+ if (va == BASELINE) |
+ return vpos; |
+ |
+ const Font& f = parent()->style(firstLine)->font(); |
+ int fontsize = f.pixelSize(); |
+ |
+ if (va == SUB) |
+ vpos += fontsize / 5 + 1; |
+ else if (va == SUPER) |
+ vpos -= fontsize / 3 + 1; |
+ else if (va == TEXT_TOP) |
+ vpos += baselinePosition(firstLine) - f.ascent(); |
+ else if (va == MIDDLE) |
+ vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); |
+ else if (va == TEXT_BOTTOM) { |
+ vpos += f.descent(); |
+ if (!isReplaced()) |
+ vpos -= style(firstLine)->font().descent(); |
+ } else if (va == BASELINE_MIDDLE) |
+ vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); |
+ else if (va == LENGTH) |
+ vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); |
+ } |
+ |
+ return vpos; |
+} |
+ |
+int RenderObject::lineHeight(bool firstLine, bool /*isRootLineBox*/) const |
+{ |
+ return style(firstLine)->computedLineHeight(); |
+} |
+ |
+int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const |
+{ |
+ const Font& f = style(firstLine)->font(); |
+ return f.ascent() + (lineHeight(firstLine, isRootLineBox) - f.height()) / 2; |
+} |
+ |
+void RenderObject::scheduleRelayout() |
+{ |
+ if (isRenderView()) { |
+ FrameView* view = toRenderView(this)->frameView(); |
+ if (view) |
+ view->scheduleRelayout(); |
+ } else if (parent()) { |
+ FrameView* v = view() ? view()->frameView() : 0; |
+ if (v) |
+ v->scheduleRelayoutOfSubtree(this); |
+ } |
+} |
+ |
+void RenderObject::layout() |
+{ |
+ ASSERT(needsLayout()); |
+ RenderObject* child = firstChild(); |
+ while (child) { |
+ child->layoutIfNeeded(); |
+ ASSERT(!child->needsLayout()); |
+ child = child->nextSibling(); |
+ } |
+ setNeedsLayout(false); |
+} |
+ |
+InlineBox* RenderObject::createInlineBox(bool, bool unusedIsRootLineBox, bool) |
+{ |
+ ASSERT_UNUSED(unusedIsRootLineBox, !unusedIsRootLineBox); |
+ return new (renderArena()) InlineBox(this); |
+} |
+ |
+void RenderObject::dirtyLineBoxes(bool, bool) |
+{ |
+} |
+ |
+InlineBox* RenderObject::inlineBoxWrapper() const |
+{ |
+ return 0; |
+} |
+ |
+void RenderObject::setInlineBoxWrapper(InlineBox*) |
+{ |
+} |
+ |
+void RenderObject::deleteLineBoxWrapper() |
+{ |
+} |
+ |
+RenderStyle* RenderObject::firstLineStyleSlowCase() const |
+{ |
+ ASSERT(document()->usesFirstLineRules()); |
+ |
+ RenderStyle* style = m_style.get(); |
+ const RenderObject* renderer = isText() ? parent() : this; |
+ if (renderer->isBlockFlow()) { |
+ if (RenderBlock* firstLineBlock = renderer->firstLineBlock()) |
+ style = firstLineBlock->getCachedPseudoStyle(FIRST_LINE, style); |
+ } else if (!renderer->isAnonymous() && renderer->isRenderInline()) { |
+ RenderStyle* parentStyle = renderer->parent()->firstLineStyle(); |
+ if (parentStyle != renderer->parent()->style()) { |
+ // A first-line style is in effect. Cache a first-line style for ourselves. |
+ style->setHasPseudoStyle(FIRST_LINE_INHERITED); |
+ style = renderer->getCachedPseudoStyle(FIRST_LINE_INHERITED, parentStyle); |
+ } |
+ } |
+ |
+ return style; |
+} |
+ |
+RenderStyle* RenderObject::getCachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const |
+{ |
+ if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) |
+ return 0; |
+ |
+ RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo); |
+ if (cachedStyle) |
+ return cachedStyle; |
+ |
+ RefPtr<RenderStyle> result = getUncachedPseudoStyle(pseudo, parentStyle); |
+ if (result) |
+ return style()->addCachedPseudoStyle(result.release()); |
+ return 0; |
+} |
+ |
+PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(PseudoId pseudo, RenderStyle* parentStyle) const |
+{ |
+ if (pseudo < FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) |
+ return 0; |
+ |
+ if (!parentStyle) |
+ parentStyle = style(); |
+ |
+ Node* node = element(); |
+ while (node && !node->isElementNode()) |
+ node = node->parentNode(); |
+ if (!node) |
+ return 0; |
+ |
+ RefPtr<RenderStyle> result; |
+ if (pseudo == FIRST_LINE_INHERITED) { |
+ result = document()->styleSelector()->styleForElement(static_cast<Element*>(node), parentStyle, false); |
+ result->setStyleType(FIRST_LINE_INHERITED); |
+ } else |
+ result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(node), parentStyle); |
+ return result.release(); |
+} |
+ |
+static Color decorationColor(RenderStyle* style) |
+{ |
+ Color result; |
+ if (style->textStrokeWidth() > 0) { |
+ // Prefer stroke color if possible but not if it's fully transparent. |
+ result = style->textStrokeColor(); |
+ if (!result.isValid()) |
+ result = style->color(); |
+ if (result.alpha()) |
+ return result; |
+ } |
+ |
+ result = style->textFillColor(); |
+ if (!result.isValid()) |
+ result = style->color(); |
+ return result; |
+} |
+ |
+void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, |
+ Color& linethrough, bool quirksMode) |
+{ |
+ RenderObject* curr = this; |
+ do { |
+ int currDecs = curr->style()->textDecoration(); |
+ if (currDecs) { |
+ if (currDecs & UNDERLINE) { |
+ decorations &= ~UNDERLINE; |
+ underline = decorationColor(curr->style()); |
+ } |
+ if (currDecs & OVERLINE) { |
+ decorations &= ~OVERLINE; |
+ overline = decorationColor(curr->style()); |
+ } |
+ if (currDecs & LINE_THROUGH) { |
+ decorations &= ~LINE_THROUGH; |
+ linethrough = decorationColor(curr->style()); |
+ } |
+ } |
+ curr = curr->parent(); |
+ if (curr && curr->isRenderBlock() && toRenderBlock(curr)->inlineContinuation()) |
+ curr = toRenderBlock(curr)->inlineContinuation(); |
+ } while (curr && decorations && (!quirksMode || !curr->element() || |
+ (!curr->element()->hasTagName(aTag) && !curr->element()->hasTagName(fontTag)))); |
+ |
+ // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). |
+ if (decorations && curr) { |
+ if (decorations & UNDERLINE) |
+ underline = decorationColor(curr->style()); |
+ if (decorations & OVERLINE) |
+ overline = decorationColor(curr->style()); |
+ if (decorations & LINE_THROUGH) |
+ linethrough = decorationColor(curr->style()); |
+ } |
+} |
+ |
+void RenderObject::updateWidgetPosition() |
+{ |
+} |
+ |
+#if ENABLE(DASHBOARD_SUPPORT) |
+void RenderObject::addDashboardRegions(Vector<DashboardRegionValue>& regions) |
+{ |
+ // Convert the style regions to absolute coordinates. |
+ if (style()->visibility() != VISIBLE || !isBox()) |
+ return; |
+ |
+ RenderBox* box = toRenderBox(this); |
+ |
+ const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); |
+ unsigned i, count = styleRegions.size(); |
+ for (i = 0; i < count; i++) { |
+ StyleDashboardRegion styleRegion = styleRegions[i]; |
+ |
+ int w = box->width(); |
+ int h = box->height(); |
+ |
+ DashboardRegionValue region; |
+ region.label = styleRegion.label; |
+ region.bounds = IntRect(styleRegion.offset.left().value(), |
+ styleRegion.offset.top().value(), |
+ w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), |
+ h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); |
+ region.type = styleRegion.type; |
+ |
+ region.clip = region.bounds; |
+ computeAbsoluteRepaintRect(region.clip); |
+ if (region.clip.height() < 0) { |
+ region.clip.setHeight(0); |
+ region.clip.setWidth(0); |
+ } |
+ |
+ FloatPoint absPos = localToAbsolute(); |
+ region.bounds.setX(absPos.x() + styleRegion.offset.left().value()); |
+ region.bounds.setY(absPos.y() + styleRegion.offset.top().value()); |
+ |
+ if (document()->frame()) { |
+ float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); |
+ if (pageScaleFactor != 1.0f) { |
+ region.bounds.scale(pageScaleFactor); |
+ region.clip.scale(pageScaleFactor); |
+ } |
+ } |
+ |
+ regions.append(region); |
+ } |
+} |
+ |
+void RenderObject::collectDashboardRegions(Vector<DashboardRegionValue>& regions) |
+{ |
+ // RenderTexts don't have their own style, they just use their parent's style, |
+ // so we don't want to include them. |
+ if (isText()) |
+ return; |
+ |
+ addDashboardRegions(regions); |
+ for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) |
+ curr->collectDashboardRegions(regions); |
+} |
+#endif |
+ |
+bool RenderObject::avoidsFloats() const |
+{ |
+ return isReplaced() || hasOverflowClip() || isHR(); |
+} |
+ |
+bool RenderObject::shrinkToAvoidFloats() const |
+{ |
+ // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this |
+ // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the |
+ // current remaining width on a line. |
+ if (isInline() && !isHTMLMarquee() || !avoidsFloats()) |
+ return false; |
+ |
+ // All auto-width objects that avoid floats should always use lineWidth. |
+ return style()->width().isAuto(); |
+} |
+ |
+bool RenderObject::willRenderImage(CachedImage*) |
+{ |
+ // Without visibility we won't render (and therefore don't care about animation). |
+ if (style()->visibility() != VISIBLE) |
+ return false; |
+ |
+ // If we're not in a window (i.e., we're dormant from being put in the b/f cache or in a background tab) |
+ // then we don't want to render either. |
+ return !document()->inPageCache() && !document()->view()->isOffscreen(); |
+} |
+ |
+int RenderObject::maximalOutlineSize(PaintPhase p) const |
+{ |
+ if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) |
+ return 0; |
+ return toRenderView(document()->renderer())->maximalOutlineSize(); |
+} |
+ |
+int RenderObject::caretMinOffset() const |
+{ |
+ return 0; |
+} |
+ |
+int RenderObject::caretMaxOffset() const |
+{ |
+ if (isReplaced()) |
+ return element() ? max(1U, element()->childNodeCount()) : 1; |
+ if (isHR()) |
+ return 1; |
+ return 0; |
+} |
+ |
+unsigned RenderObject::caretMaxRenderedOffset() const |
+{ |
+ return 0; |
+} |
+ |
+int RenderObject::previousOffset(int current) const |
+{ |
+ return current - 1; |
+} |
+ |
+int RenderObject::nextOffset(int current) const |
+{ |
+ return current + 1; |
+} |
+ |
+void RenderObject::adjustRectForOutlineAndShadow(IntRect& rect) const |
+{ |
+ int outlineSize = outlineStyleForRepaint()->outlineSize(); |
+ if (ShadowData* boxShadow = style()->boxShadow()) { |
+ int shadowLeft = 0; |
+ int shadowRight = 0; |
+ int shadowTop = 0; |
+ int shadowBottom = 0; |
+ |
+ do { |
+ shadowLeft = min(boxShadow->x - boxShadow->blur - outlineSize, shadowLeft); |
+ shadowRight = max(boxShadow->x + boxShadow->blur + outlineSize, shadowRight); |
+ shadowTop = min(boxShadow->y - boxShadow->blur - outlineSize, shadowTop); |
+ shadowBottom = max(boxShadow->y + boxShadow->blur + outlineSize, shadowBottom); |
+ |
+ boxShadow = boxShadow->next; |
+ } while (boxShadow); |
+ |
+ rect.move(shadowLeft, shadowTop); |
+ rect.setWidth(rect.width() - shadowLeft + shadowRight); |
+ rect.setHeight(rect.height() - shadowTop + shadowBottom); |
+ } else |
+ rect.inflate(outlineSize); |
+} |
+ |
+AnimationController* RenderObject::animation() const |
+{ |
+ return document()->frame()->animation(); |
+} |
+ |
+void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) |
+{ |
+ imageChanged(static_cast<WrappedImagePtr>(image), rect); |
+} |
+ |
+#if ENABLE(SVG) |
+ |
+FloatRect RenderObject::relativeBBox(bool) const |
+{ |
+ return FloatRect(); |
+} |
+ |
+TransformationMatrix RenderObject::localTransform() const |
+{ |
+ return TransformationMatrix(); |
+} |
+ |
+TransformationMatrix RenderObject::absoluteTransform() const |
+{ |
+ if (parent()) |
+ return localTransform() * parent()->absoluteTransform(); |
+ return localTransform(); |
+} |
+ |
+#endif // ENABLE(SVG) |
+ |
+} // namespace WebCore |
+ |
+#ifndef NDEBUG |
+ |
+void showTree(const WebCore::RenderObject* ro) |
+{ |
+ if (ro) |
+ ro->showTreeForThis(); |
+} |
+ |
+#endif |
+ |