File indexing completed on 2024-11-10 09:39:03
0001 /** 0002 * This file is part of the html renderer for KDE. 0003 * 0004 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) 0005 * (C) 1999 Antti Koivisto (koivisto@kde.org) 0006 * (C) 2000-2003 Dirk Mueller (mueller@kde.org) 0007 * (C) 2004-2008 Apple Computer, Inc. 0008 * (C) 2006 Germain Garand <germain@ebooksfrance.org> 0009 * (C) 2008-2009 Fredrik Höglund <fredrik@kde.org> 0010 * 0011 * This library is free software; you can redistribute it and/or 0012 * modify it under the terms of the GNU Library General Public 0013 * License as published by the Free Software Foundation; either 0014 * version 2 of the License, or (at your option) any later version. 0015 * 0016 * This library is distributed in the hope that it will be useful, 0017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0019 * Library General Public License for more details. 0020 * 0021 * You should have received a copy of the GNU Library General Public License 0022 * along with this library; see the file COPYING.LIB. If not, write to 0023 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0024 * Boston, MA 02110-1301, USA. 0025 * 0026 */ 0027 0028 #include "rendering/render_object.h" 0029 #include "rendering/render_table.h" 0030 #include "rendering/render_list.h" 0031 #include "rendering/render_canvas.h" 0032 #include "rendering/render_block.h" 0033 #include "rendering/render_arena.h" 0034 #include "rendering/render_layer.h" 0035 #include "rendering/render_line.h" 0036 #include "rendering/render_inline.h" 0037 #include "rendering/render_text.h" 0038 #include "rendering/render_replaced.h" 0039 #include "rendering/render_generated.h" 0040 #include "rendering/counter_tree.h" 0041 0042 #include "xml/dom_elementimpl.h" 0043 #include "dom/dom_doc.h" 0044 #include "misc/loader.h" 0045 #include "misc/borderarcstroker.h" 0046 0047 #include "khtml_debug.h" 0048 #include <QPainter> 0049 #include "khtmlview.h" 0050 #include <khtml_part.h> 0051 #include <QPaintEngine> 0052 0053 using namespace DOM; 0054 using namespace khtml; 0055 0056 #define RED_LUMINOSITY 30 0057 #define GREEN_LUMINOSITY 59 0058 #define BLUE_LUMINOSITY 11 0059 #define INTENSITY_FACTOR 25 0060 #define LIGHT_FACTOR 0 0061 #define LUMINOSITY_FACTOR 75 0062 0063 #define MAX_COLOR 255 0064 #define COLOR_DARK_THRESHOLD 51 0065 #define COLOR_LIGHT_THRESHOLD 204 0066 0067 #define COLOR_LITE_BS_FACTOR 45 0068 #define COLOR_LITE_TS_FACTOR 70 0069 0070 #define COLOR_DARK_BS_FACTOR 30 0071 #define COLOR_DARK_TS_FACTOR 50 0072 0073 #define LIGHT_GRAY qRgb(192, 192, 192) 0074 #define DARK_GRAY qRgb(96, 96, 96) 0075 0076 #ifndef NDEBUG 0077 static void *baseOfRenderObjectBeingDeleted; 0078 #endif 0079 0080 QCache<quint64, QPixmap> *RenderObject::s_dashedLineCache = nullptr; 0081 0082 void RenderObject::cleanup() 0083 { 0084 delete s_dashedLineCache; 0085 s_dashedLineCache = nullptr; 0086 } 0087 0088 //#define MASK_DEBUG 0089 0090 void *RenderObject::operator new(size_t sz, RenderArena *renderArena) throw() 0091 { 0092 return renderArena->allocate(sz); 0093 } 0094 0095 void RenderObject::operator delete(void *ptr, size_t sz) 0096 { 0097 assert(baseOfRenderObjectBeingDeleted == ptr); 0098 0099 #ifdef KHTML_USE_ARENA_ALLOCATOR 0100 // Stash size where detach can find it. 0101 *(size_t *)ptr = sz; 0102 #endif 0103 } 0104 0105 RenderObject *RenderObject::createObject(DOM::NodeImpl *node, RenderStyle *style) 0106 { 0107 RenderObject *o = nullptr; 0108 khtml::RenderArena *arena = node->document()->renderArena(); 0109 switch (style->display()) { 0110 case NONE: 0111 break; 0112 case INLINE: 0113 o = new(arena) RenderInline(node); 0114 break; 0115 case BLOCK: 0116 o = new(arena) RenderBlock(node); 0117 break; 0118 case INLINE_BLOCK: 0119 o = new(arena) RenderBlock(node); 0120 break; 0121 case LIST_ITEM: 0122 o = new(arena) RenderListItem(node); 0123 break; 0124 case RUN_IN: 0125 case COMPACT: 0126 o = new(arena) RenderBlock(node); 0127 break; 0128 case TABLE: 0129 case INLINE_TABLE: 0130 style->setFlowAroundFloats(true); 0131 o = new(arena) RenderTable(node); 0132 break; 0133 case TABLE_ROW_GROUP: 0134 case TABLE_HEADER_GROUP: 0135 case TABLE_FOOTER_GROUP: 0136 o = new(arena) RenderTableSection(node); 0137 break; 0138 case TABLE_ROW: 0139 o = new(arena) RenderTableRow(node); 0140 break; 0141 case TABLE_COLUMN_GROUP: 0142 case TABLE_COLUMN: 0143 o = new(arena) RenderTableCol(node); 0144 break; 0145 case TABLE_CELL: 0146 o = new(arena) RenderTableCell(node); 0147 break; 0148 case TABLE_CAPTION: 0149 o = new(arena) RenderBlock(node); 0150 break; 0151 } 0152 return o; 0153 } 0154 0155 RenderObject::RenderObject(DOM::NodeImpl *node) 0156 : CachedObjectClient(), 0157 m_style(nullptr), 0158 m_node(node), 0159 m_parent(nullptr), 0160 m_previous(nullptr), 0161 m_next(nullptr), 0162 m_verticalPosition(PositionUndefined), 0163 m_needsLayout(false), 0164 m_normalChildNeedsLayout(false), 0165 m_markedForRepaint(false), 0166 m_posChildNeedsLayout(false), 0167 m_minMaxKnown(false), 0168 m_floating(false), 0169 0170 m_positioned(false), 0171 m_relPositioned(false), 0172 m_paintBackground(false), 0173 0174 m_isAnonymous(node->isDocumentNode()), 0175 m_recalcMinMax(false), 0176 m_isText(false), 0177 m_inline(true), 0178 m_attached(false), 0179 0180 m_replaced(false), 0181 m_mouseInside(false), 0182 m_hasFirstLine(false), 0183 m_isSelectionBorder(false), 0184 m_isRoot(false), 0185 m_afterPageBreak(false), 0186 m_needsPageClear(false), 0187 m_containsPageBreak(false), 0188 m_hasOverflowClip(false), 0189 m_inPosObjectList(false), 0190 m_doNotDelete(false) 0191 { 0192 assert(node); 0193 if (node->document()->documentElement() == node) { 0194 setIsRoot(true); 0195 } 0196 } 0197 0198 RenderObject::~RenderObject() 0199 { 0200 const BackgroundLayer *bgLayer = m_style->backgroundLayers(); 0201 while (bgLayer) { 0202 if (bgLayer->backgroundImage()) { 0203 bgLayer->backgroundImage()->deref(this); 0204 } 0205 bgLayer = bgLayer->next(); 0206 } 0207 0208 m_style->deref(); 0209 } 0210 0211 RenderObject *RenderObject::objectBelow() const 0212 { 0213 RenderObject *obj = firstChild(); 0214 if (!obj) { 0215 obj = nextSibling(); 0216 if (!obj) { 0217 obj = parent(); 0218 while (obj && !obj->nextSibling()) { 0219 obj = obj->parent(); 0220 } 0221 if (obj) { 0222 obj = obj->nextSibling(); 0223 } 0224 } 0225 } 0226 return obj; 0227 } 0228 0229 RenderObject *RenderObject::objectAbove() const 0230 { 0231 RenderObject *obj = previousSibling(); 0232 if (!obj) { 0233 return parent(); 0234 } 0235 0236 RenderObject *last = obj->lastChild(); 0237 while (last) { 0238 obj = last; 0239 last = last->lastChild(); 0240 } 0241 return obj; 0242 } 0243 /* 0244 bool RenderObject::isRoot() const 0245 { 0246 return !isAnonymous() && 0247 element()->document()->documentElement() == element(); 0248 }*/ 0249 0250 bool RenderObject::isHR() const 0251 { 0252 return element() && element()->id() == ID_HR; 0253 } 0254 bool RenderObject::isWordBreak() const 0255 { 0256 return element() && element()->id() == ID_WBR; 0257 } 0258 bool RenderObject::isHTMLMarquee() const 0259 { 0260 return element() && element()->renderer() == this && element()->id() == ID_MARQUEE; 0261 } 0262 0263 void RenderObject::addChild(RenderObject *, RenderObject *) 0264 { 0265 KHTMLAssert(0); 0266 } 0267 0268 RenderObject *RenderObject::removeChildNode(RenderObject *) 0269 { 0270 KHTMLAssert(0); 0271 return nullptr; 0272 } 0273 0274 void RenderObject::removeChild(RenderObject *) 0275 { 0276 KHTMLAssert(0); 0277 } 0278 0279 void RenderObject::appendChildNode(RenderObject *) 0280 { 0281 KHTMLAssert(0); 0282 } 0283 0284 void RenderObject::insertChildNode(RenderObject *, RenderObject *) 0285 { 0286 KHTMLAssert(0); 0287 } 0288 0289 RenderObject *RenderObject::nextRenderer() const 0290 { 0291 if (firstChild()) { 0292 return firstChild(); 0293 } else if (nextSibling()) { 0294 return nextSibling(); 0295 } else { 0296 const RenderObject *r = this; 0297 while (r && !r->nextSibling()) { 0298 r = r->parent(); 0299 } 0300 if (r) { 0301 return r->nextSibling(); 0302 } 0303 } 0304 return nullptr; 0305 } 0306 0307 RenderObject *RenderObject::previousRenderer() const 0308 { 0309 if (previousSibling()) { 0310 RenderObject *r = previousSibling(); 0311 while (r->lastChild()) { 0312 r = r->lastChild(); 0313 } 0314 return r; 0315 } else if (parent()) { 0316 return parent(); 0317 } else { 0318 return nullptr; 0319 } 0320 } 0321 0322 bool RenderObject::isEditable() const 0323 { 0324 RenderText *textRenderer = nullptr; 0325 if (isText()) { 0326 textRenderer = static_cast<RenderText *>(const_cast<RenderObject *>(this)); 0327 } 0328 0329 return style()->visibility() == VISIBLE && 0330 element() && element()->isContentEditable() && 0331 ((isBlockFlow() && !firstChild()) || 0332 isReplaced() || 0333 isBR() || 0334 (textRenderer && textRenderer->firstTextBox())); 0335 } 0336 0337 RenderObject *RenderObject::nextEditable() const 0338 { 0339 RenderObject *r = const_cast<RenderObject *>(this); 0340 RenderObject *n = firstChild(); 0341 if (n) { 0342 while (n) { 0343 r = n; 0344 n = n->firstChild(); 0345 } 0346 if (r->isEditable()) { 0347 return r; 0348 } else { 0349 return r->nextEditable(); 0350 } 0351 } 0352 n = r->nextSibling(); 0353 if (n) { 0354 r = n; 0355 while (n) { 0356 r = n; 0357 n = n->firstChild(); 0358 } 0359 if (r->isEditable()) { 0360 return r; 0361 } else { 0362 return r->nextEditable(); 0363 } 0364 } 0365 n = r->parent(); 0366 while (n) { 0367 r = n; 0368 n = r->nextSibling(); 0369 if (n) { 0370 r = n; 0371 n = r->firstChild(); 0372 while (n) { 0373 r = n; 0374 n = n->firstChild(); 0375 } 0376 if (r->isEditable()) { 0377 return r; 0378 } else { 0379 return r->nextEditable(); 0380 } 0381 } 0382 n = r->parent(); 0383 } 0384 return nullptr; 0385 } 0386 0387 RenderObject *RenderObject::previousEditable() const 0388 { 0389 RenderObject *r = const_cast<RenderObject *>(this); 0390 RenderObject *n = firstChild(); 0391 if (n) { 0392 while (n) { 0393 r = n; 0394 n = n->lastChild(); 0395 } 0396 if (r->isEditable()) { 0397 return r; 0398 } else { 0399 return r->previousEditable(); 0400 } 0401 } 0402 n = r->previousSibling(); 0403 if (n) { 0404 r = n; 0405 while (n) { 0406 r = n; 0407 n = n->lastChild(); 0408 } 0409 if (r->isEditable()) { 0410 return r; 0411 } else { 0412 return r->previousEditable(); 0413 } 0414 } 0415 n = r->parent(); 0416 while (n) { 0417 r = n; 0418 n = r->previousSibling(); 0419 if (n) { 0420 r = n; 0421 n = r->lastChild(); 0422 while (n) { 0423 r = n; 0424 n = n->lastChild(); 0425 } 0426 if (r->isEditable()) { 0427 return r; 0428 } else { 0429 return r->previousEditable(); 0430 } 0431 } 0432 n = r->parent(); 0433 } 0434 return nullptr; 0435 } 0436 0437 RenderObject *RenderObject::firstLeafChild() const 0438 { 0439 RenderObject *r = firstChild(); 0440 while (r) { 0441 RenderObject *n = nullptr; 0442 n = r->firstChild(); 0443 if (!n) { 0444 break; 0445 } 0446 r = n; 0447 } 0448 return r; 0449 } 0450 0451 RenderObject *RenderObject::lastLeafChild() const 0452 { 0453 RenderObject *r = lastChild(); 0454 while (r) { 0455 RenderObject *n = nullptr; 0456 n = r->lastChild(); 0457 if (!n) { 0458 break; 0459 } 0460 r = n; 0461 } 0462 return r; 0463 } 0464 0465 static void addLayers(RenderObject *obj, RenderLayer *parentLayer, RenderObject *&newObject, 0466 RenderLayer *&beforeChild) 0467 { 0468 if (obj->layer()) { 0469 if (!beforeChild && newObject) { 0470 // We need to figure out the layer that follows newObject. We only do 0471 // this the first time we find a child layer, and then we update the 0472 // pointer values for newObject and beforeChild used by everyone else. 0473 beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); 0474 newObject = nullptr; 0475 } 0476 parentLayer->addChild(obj->layer(), beforeChild); 0477 return; 0478 } 0479 0480 for (RenderObject *curr = obj->firstChild(); curr; curr = curr->nextSibling()) { 0481 addLayers(curr, parentLayer, newObject, beforeChild); 0482 } 0483 } 0484 0485 void RenderObject::addLayers(RenderLayer *parentLayer, RenderObject *newObject) 0486 { 0487 if (!parentLayer) { 0488 return; 0489 } 0490 0491 RenderObject *object = newObject; 0492 RenderLayer *beforeChild = nullptr; 0493 ::addLayers(this, parentLayer, object, beforeChild); 0494 } 0495 0496 void RenderObject::removeLayers(RenderLayer *parentLayer) 0497 { 0498 if (!parentLayer) { 0499 return; 0500 } 0501 0502 if (layer()) { 0503 parentLayer->removeChild(layer()); 0504 return; 0505 } 0506 0507 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 0508 curr->removeLayers(parentLayer); 0509 } 0510 } 0511 0512 void RenderObject::moveLayers(RenderLayer *oldParent, RenderLayer *newParent) 0513 { 0514 if (!newParent) { 0515 return; 0516 } 0517 0518 if (layer()) { 0519 if (oldParent) { 0520 oldParent->removeChild(layer()); 0521 } 0522 newParent->addChild(layer()); 0523 return; 0524 } 0525 0526 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 0527 curr->moveLayers(oldParent, newParent); 0528 } 0529 } 0530 0531 RenderLayer *RenderObject::findNextLayer(RenderLayer *parentLayer, RenderObject *startPoint, 0532 bool checkParent) 0533 { 0534 // Error check the parent layer passed in. If it's null, we can't find anything. 0535 if (!parentLayer) { 0536 return nullptr; 0537 } 0538 0539 // Step 1: If our layer is a child of the desired parent, then return our layer. 0540 RenderLayer *ourLayer = layer(); 0541 if (ourLayer && ourLayer->parent() == parentLayer) { 0542 return ourLayer; 0543 } 0544 0545 // Step 2: If we don't have a layer, or our layer is the desired parent, then descend 0546 // into our siblings trying to find the next layer whose parent is the desired parent. 0547 if (!ourLayer || ourLayer == parentLayer) { 0548 for (RenderObject *curr = startPoint ? startPoint->nextSibling() : firstChild(); 0549 curr; curr = curr->nextSibling()) { 0550 RenderLayer *nextLayer = curr->findNextLayer(parentLayer, nullptr, false); 0551 if (nextLayer) { 0552 return nextLayer; 0553 } 0554 } 0555 } 0556 0557 // Step 3: If our layer is the desired parent layer, then we're finished. We didn't 0558 // find anything. 0559 if (parentLayer == ourLayer) { 0560 return nullptr; 0561 } 0562 0563 // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that 0564 // follow us to see if we can locate a layer. 0565 if (checkParent && parent()) { 0566 return parent()->findNextLayer(parentLayer, this, true); 0567 } 0568 0569 return nullptr; 0570 } 0571 0572 RenderLayer *RenderObject::enclosingLayer() const 0573 { 0574 const RenderObject *curr = this; 0575 while (curr) { 0576 RenderLayer *layer = curr->layer(); 0577 if (layer) { 0578 return layer; 0579 } 0580 curr = curr->parent(); 0581 } 0582 return nullptr; 0583 } 0584 0585 RenderLayer *RenderObject::enclosingStackingContext() const 0586 { 0587 RenderLayer *l = enclosingLayer(); 0588 while (l && !l->isStackingContext()) { 0589 l = l->parent(); 0590 } 0591 return l; 0592 } 0593 0594 QRectF RenderObject::clientRectToViewport(const QRectF &rect) 0595 { 0596 int offsetX = document()->part()->view()->contentsX(); 0597 int offsetY = document()->part()->view()->contentsY(); 0598 0599 QRectF newRect(rect.x() - offsetX, rect.y() - offsetY, 0600 rect.width(), rect.height()); 0601 0602 return newRect; 0603 } 0604 0605 QList<QRectF> RenderObject::getClientRects() 0606 { 0607 QList<QRectF> ret; 0608 0609 int x = 0; 0610 int y = 0; 0611 absolutePosition(x, y); 0612 0613 QRectF rect(x, y, width(), height()); 0614 ret.append(clientRectToViewport(rect)); 0615 0616 return ret; 0617 } 0618 0619 int RenderObject::offsetLeft() const 0620 { 0621 if (isBody()) { 0622 return 0; 0623 } 0624 0625 int x, dummy; 0626 RenderObject *offsetPar = offsetParent(); 0627 if (!offsetPar || offsetPar->isBody()) { 0628 if (style()->position() == PFIXED) { 0629 return xPos(); 0630 } else { 0631 absolutePosition(x, dummy); 0632 return x; 0633 } 0634 } 0635 0636 x = xPos() - offsetPar->borderLeft(); 0637 if (isPositioned()) { 0638 return x; 0639 } 0640 0641 if (isRelPositioned()) { 0642 int y = 0; 0643 static_cast<const RenderBox *>(this)->relativePositionOffset(x, y); 0644 } 0645 0646 for (RenderObject *curr = parent(); 0647 curr && curr != offsetPar; 0648 curr = curr->parent()) { 0649 x += curr->xPos(); 0650 } 0651 0652 return x; 0653 } 0654 0655 int RenderObject::offsetTop() const 0656 { 0657 if (isBody()) { 0658 return 0; 0659 } 0660 0661 int y, dummy; 0662 RenderObject *offsetPar = offsetParent(); 0663 if (!offsetPar || offsetPar->isBody()) { 0664 if (style()->position() == PFIXED) { 0665 return yPos(); 0666 } else { 0667 absolutePosition(dummy, y); 0668 return y; 0669 } 0670 } 0671 0672 y = yPos() - offsetPar->borderTop(); 0673 if (isPositioned()) { 0674 return y; 0675 } 0676 0677 if (isRelPositioned()) { 0678 int x = 0; 0679 static_cast<const RenderBox *>(this)->relativePositionOffset(x, y); 0680 } 0681 for (RenderObject *curr = parent(); 0682 curr && curr != offsetPar; 0683 curr = curr->parent()) { 0684 y += curr->yPos(); 0685 } 0686 0687 return y; 0688 } 0689 0690 RenderObject *RenderObject::offsetParent() const 0691 { 0692 if (isBody() || style()->position() == PFIXED) { 0693 return nullptr; 0694 } 0695 0696 // can't really use containing blocks here (#113280) 0697 bool skipTables = isPositioned() || isRelPositioned(); 0698 bool strict = !style()->htmlHacks(); 0699 RenderObject *curr = parent(); 0700 while (curr && (!curr->element() || 0701 (!curr->isPositioned() && !curr->isRelPositioned() && 0702 !(strict && skipTables ? curr->isRoot() : curr->isBody())))) { 0703 if (!skipTables && curr->element() && (curr->isTableCell() || curr->isTable())) { 0704 break; 0705 } 0706 curr = curr->parent(); 0707 } 0708 return curr; 0709 } 0710 0711 // IE extensions. 0712 // clientWidth and clientHeight represent the interior of an object 0713 short RenderObject::clientWidth() const 0714 { 0715 return width() - borderLeft() - borderRight() - 0716 (layer() ? layer()->verticalScrollbarWidth() : 0); 0717 } 0718 0719 int RenderObject::clientLeft() const 0720 { 0721 return borderLeft(); 0722 } 0723 0724 int RenderObject::clientTop() const 0725 { 0726 return borderTop(); 0727 } 0728 0729 int RenderObject::clientHeight() const 0730 { 0731 return height() - borderTop() - borderBottom() - 0732 (layer() ? layer()->horizontalScrollbarHeight() : 0); 0733 } 0734 0735 // scrollWidth/scrollHeight is the size including the overflow area 0736 short RenderObject::scrollWidth() const 0737 { 0738 return (hasOverflowClip() && layer()) ? layer()->scrollWidth() : overflowWidth() - overflowLeft(); 0739 } 0740 0741 int RenderObject::scrollHeight() const 0742 { 0743 return (hasOverflowClip() && layer()) ? layer()->scrollHeight() : overflowHeight() - overflowTop(); 0744 } 0745 0746 void RenderObject::updatePixmap(const QRect & /*r*/, CachedImage *image) 0747 { 0748 #ifdef __GNUC__ 0749 #warning "FIXME: Check if complete!" 0750 #endif 0751 //repaint bg when it finished loading 0752 if (image && parent() && style() && style()->backgroundLayers()->containsImage(image)) { 0753 isBody() ? canvas()->repaint() : repaint(); 0754 } 0755 } 0756 0757 void RenderObject::setNeedsLayout(bool b, bool markParents) 0758 { 0759 bool alreadyNeededLayout = m_needsLayout; 0760 m_needsLayout = b; 0761 if (b) { 0762 if (!alreadyNeededLayout && markParents && m_parent) { 0763 dirtyFormattingContext(false); 0764 markContainingBlocksForLayout(); 0765 } 0766 } else { 0767 m_posChildNeedsLayout = false; 0768 m_normalChildNeedsLayout = false; 0769 } 0770 } 0771 0772 void RenderObject::setChildNeedsLayout(bool b, bool markParents) 0773 { 0774 bool alreadyNeededLayout = m_normalChildNeedsLayout; 0775 m_normalChildNeedsLayout = b; 0776 if (b) { 0777 if (!alreadyNeededLayout && markParents) { 0778 markContainingBlocksForLayout(); 0779 } 0780 } else { 0781 m_posChildNeedsLayout = false; 0782 m_normalChildNeedsLayout = false; 0783 } 0784 } 0785 0786 void RenderObject::markContainingBlocksForLayout() 0787 { 0788 RenderObject *o = container(); 0789 RenderObject *last = this; 0790 0791 while (o) { 0792 if (!last->isText() && (last->style()->position() == PFIXED || last->style()->position() == PABSOLUTE)) { 0793 if (o->m_posChildNeedsLayout) { 0794 return; 0795 } 0796 o->m_posChildNeedsLayout = true; 0797 } else { 0798 if (o->m_normalChildNeedsLayout) { 0799 return; 0800 } 0801 o->m_normalChildNeedsLayout = true; 0802 } 0803 0804 last = o; 0805 o = o->container(); 0806 } 0807 0808 last->scheduleRelayout(); 0809 } 0810 0811 RenderBlock *RenderObject::containingBlock() const 0812 { 0813 if (isTableCell()) { 0814 return static_cast<RenderBlock *>(parent()->parent()->parent()); 0815 } 0816 if (isCanvas()) { 0817 return const_cast<RenderBlock *>(static_cast<const RenderBlock *>(this)); 0818 } 0819 0820 RenderObject *o = parent(); 0821 if (m_style->position() == PFIXED) { 0822 while (o && !o->isCanvas()) { 0823 o = o->parent(); 0824 } 0825 } else if (m_style->position() == PABSOLUTE) { 0826 while (o && 0827 (o->style()->position() == PSTATIC || (o->isInline() && !o->isReplaced())) && !o->isCanvas()) { 0828 // for relpos inlines, return the nearest block - it will host the positioned objects list 0829 if (o->isInline() && !o->isReplaced() && o->style()->position() == PRELATIVE) { 0830 return o->containingBlock(); 0831 } 0832 o = o->parent(); 0833 } 0834 } else { 0835 while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection() || 0836 o->isTableCol() || o->isFrameSet() || 0837 o->isSVGContainer() || o->isSVGRoot())) // for svg 0838 0839 { 0840 o = o->parent(); 0841 } 0842 } 0843 // this is just to make sure we return a valid element. 0844 // the case below should never happen... 0845 if (!o || !o->isRenderBlock()) { 0846 if (!isCanvas()) { 0847 #ifndef NDEBUG 0848 qCDebug(KHTML_LOG) << this << ": " << renderName() << "(RenderObject): No containingBlock!"; 0849 const RenderObject *p = this; 0850 while (p->parent()) { 0851 p = p->parent(); 0852 } 0853 p->printTree(); 0854 #endif 0855 } 0856 return canvas(); // likely wrong, but better than a crash 0857 } 0858 0859 return static_cast<RenderBlock *>(o); 0860 } 0861 0862 short RenderObject::containingBlockWidth(RenderObject *) const 0863 { 0864 // ### 0865 return containingBlock()->contentWidth(); 0866 } 0867 0868 int RenderObject::containingBlockHeight(RenderObject *) const 0869 { 0870 // ### 0871 return containingBlock()->contentHeight(); 0872 } 0873 0874 bool RenderObject::sizesToMaxWidth() const 0875 { 0876 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, 0877 // but they allow text to sit on the same line as the marquee. 0878 if (isFloating() || isCompact() || 0879 (isInlineBlockOrInlineTable() && !isHTMLMarquee()) || 0880 (element() && (element()->id() == ID_BUTTON || element()->id() == ID_LEGEND))) { 0881 return true; 0882 } 0883 0884 // Children of a horizontal marquee do not fill the container by default. 0885 // FIXME: Need to deal with MAUTO value properly. It could be vertical. 0886 if (parent()->style()->overflowX() == OMARQUEE) { 0887 EMarqueeDirection dir = parent()->style()->marqueeDirection(); 0888 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) { 0889 return true; 0890 } 0891 } 0892 0893 #ifdef APPLE_CHANGES // ### what the heck is a flexbox? 0894 // Flexible horizontal boxes lay out children at their maxwidths. Also vertical boxes 0895 // that don't stretch their kids lay out their children at their maxwidths. 0896 if (parent()->isFlexibleBox() && 0897 (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) { 0898 return true; 0899 } 0900 #endif 0901 0902 return false; 0903 } 0904 0905 // from Mozilla's nsCSSColorUtils.cpp 0906 static int brightness(int red, int green, int blue) 0907 { 0908 0909 int intensity = (red + green + blue) / 3; 0910 0911 int luminosity = 0912 ((RED_LUMINOSITY * red) / 100) + 0913 ((GREEN_LUMINOSITY * green) / 100) + 0914 ((BLUE_LUMINOSITY * blue) / 100); 0915 0916 return ((intensity * INTENSITY_FACTOR) + 0917 (luminosity * LUMINOSITY_FACTOR)) / 100; 0918 } 0919 0920 static void calc3DColor(QColor &color, bool darken) 0921 { 0922 int rb = color.red(); 0923 int gb = color.green(); 0924 int bb = color.blue(); 0925 int a = color.alpha(); 0926 0927 int brightness_ = brightness(rb, gb, bb); 0928 0929 int f0, f1; 0930 if (brightness_ < COLOR_DARK_THRESHOLD) { 0931 f0 = COLOR_DARK_BS_FACTOR; 0932 f1 = COLOR_DARK_TS_FACTOR; 0933 } else if (brightness_ > COLOR_LIGHT_THRESHOLD) { 0934 f0 = COLOR_LITE_BS_FACTOR; 0935 f1 = COLOR_LITE_TS_FACTOR; 0936 } else { 0937 f0 = COLOR_DARK_BS_FACTOR + 0938 (brightness_ * 0939 (COLOR_LITE_BS_FACTOR - COLOR_DARK_BS_FACTOR) / MAX_COLOR); 0940 f1 = COLOR_DARK_TS_FACTOR + 0941 (brightness_ * 0942 (COLOR_LITE_TS_FACTOR - COLOR_DARK_TS_FACTOR) / MAX_COLOR); 0943 } 0944 0945 if (darken) { 0946 int r = rb - (f0 * rb / 100); 0947 int g = gb - (f0 * gb / 100); 0948 int b = bb - (f0 * bb / 100); 0949 if ((r == rb) && (g == gb) && (b == bb)) { 0950 color = (color == Qt::black) ? QColor(DARK_GRAY) : QColor(Qt::black); 0951 } else { 0952 color.setRgb(r, g, b); 0953 } 0954 } else { 0955 int r = qMin(rb + (f1 * (MAX_COLOR - rb) / 100), 255); 0956 int g = qMin(gb + (f1 * (MAX_COLOR - gb) / 100), 255); 0957 int b = qMin(bb + (f1 * (MAX_COLOR - bb) / 100), 255); 0958 if ((r == rb) && (g == gb) && (b == bb)) { 0959 color = (color == Qt::white) ? QColor(LIGHT_GRAY) : QColor(Qt::white); 0960 } else { 0961 color.setRgb(r, g, b); 0962 } 0963 } 0964 color.setAlpha(a); 0965 } 0966 0967 void RenderObject::drawBorder(QPainter *p, int x1, int y1, int x2, int y2, 0968 BorderSide s, QColor c, const QColor &textcolor, EBorderStyle style, 0969 int adjbw1, int adjbw2, bool invalidisInvert, qreal *nextDashOffset) 0970 { 0971 if (nextDashOffset && style != DOTTED && style != DASHED) { 0972 *nextDashOffset = 0; 0973 } 0974 0975 if (p->hasClipping() && !p->clipRegion().boundingRect().intersects(QRect(x1, y1, x2 - x1, y2 - y1))) { 0976 if (nextDashOffset && (style == DOTTED || style == DASHED)) { 0977 *nextDashOffset += (s == BSTop || s == BSBottom) ? (x2 - x1) : (y2 - y1); 0978 } 0979 return; 0980 } 0981 0982 int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1); 0983 0984 if (style == DOUBLE && width < 3) { 0985 style = SOLID; 0986 } 0987 0988 if (!c.isValid()) { 0989 if (invalidisInvert) { 0990 // handle 'outline-color: invert' 0991 if (p->paintEngine() && p->paintEngine()->hasFeature(QPaintEngine::BlendModes)) { 0992 p->setCompositionMode(QPainter::CompositionMode_Difference); 0993 c = Qt::white; 0994 } else { 0995 // 'invert' is not supported on this platform (for instance XRender) 0996 // CSS3 UI 8.4: If the UA does not support the 'invert' value then the initial value of 0997 // the 'outline-color' property is the 'currentColor' [CSS3COLOR] keyword. 0998 c = m_style->color(); 0999 } 1000 } else { 1001 if (style == INSET || style == OUTSET || style == RIDGE || style == 1002 GROOVE) { 1003 c = Qt::white; 1004 } else { 1005 c = textcolor; 1006 } 1007 } 1008 } 1009 1010 switch (style) { 1011 case BNATIVE: 1012 case BNONE: 1013 case BHIDDEN: 1014 // should not happen 1015 if (invalidisInvert && p->compositionMode() == QPainter::CompositionMode_Difference) { 1016 p->setCompositionMode(QPainter::CompositionMode_SourceOver); 1017 } 1018 1019 return; 1020 case DOTTED: 1021 case DASHED: { 1022 if (width <= 0) { 1023 break; 1024 } 1025 1026 //Figure out on/off spacing 1027 int onLen = width; 1028 int offLen = width; 1029 1030 if (style == DASHED) { 1031 if (width == 1) { 1032 onLen = 3; 1033 offLen = 3; 1034 } else { 1035 onLen = width * 3; 1036 offLen = width; 1037 } 1038 } 1039 1040 // Compute the offset for the dash pattern, taking the direction of 1041 // the line into account. (The borders are drawn counter-clockwise) 1042 QPoint offset(0, 0); 1043 if (nextDashOffset) { 1044 switch (s) { 1045 // The left border is drawn top to bottom 1046 case BSLeft: 1047 offset.ry() = -qRound(*nextDashOffset); 1048 *nextDashOffset += (y2 - y1); 1049 break; 1050 1051 // The bottom border is drawn left to right 1052 case BSBottom: 1053 offset.rx() = -qRound(*nextDashOffset); 1054 *nextDashOffset += (x2 - x1); 1055 break; 1056 1057 // The top border is drawn right to left 1058 case BSTop: 1059 offset.rx() = (x2 - x1) + offLen + qRound(*nextDashOffset); 1060 *nextDashOffset += (x2 - x1); 1061 break; 1062 1063 // The right border is drawn bottom to top 1064 case BSRight: 1065 offset.ry() = (y2 - y1) + offLen + qRound(*nextDashOffset); 1066 *nextDashOffset += (y2 - y1); 1067 break; 1068 } 1069 1070 offset.rx() = offset.x() % (onLen + offLen); 1071 offset.ry() = offset.y() % (onLen + offLen); 1072 } 1073 1074 if ((onLen + offLen) <= 32 && width < 0x7fff) { 1075 if (!s_dashedLineCache) { 1076 s_dashedLineCache = new QCache<quint64, QPixmap>(30); 1077 } 1078 1079 bool horizontal = (s == BSBottom || s == BSTop); 1080 quint64 key = int(horizontal) << 31 | (onLen & 0xff) << 23 | (offLen & 0xff) << 15 | (width & 0x7fff); 1081 key = key << 32 | c.rgba(); 1082 1083 QPixmap *tilePtr = s_dashedLineCache->object(key); 1084 QPixmap tile; 1085 if (!tilePtr) { 1086 QPainterPath path; 1087 int size = (onLen + offLen) * (64 / (onLen + offLen)); 1088 if (horizontal) { 1089 tilePtr = new QPixmap(size, width); 1090 tilePtr->fill(Qt::transparent); 1091 for (int x = 0; x < tilePtr->width(); x += onLen + offLen) { 1092 path.addRect(x, 0, onLen, tilePtr->height()); 1093 } 1094 } else { //Vertical 1095 tilePtr = new QPixmap(width, size); 1096 tilePtr->fill(Qt::transparent); 1097 for (int y = 0; y < tilePtr->height(); y += onLen + offLen) { 1098 path.addRect(0, y, tilePtr->width(), onLen); 1099 } 1100 } 1101 QPainter p2(tilePtr); 1102 p2.fillPath(path, c); 1103 p2.end(); 1104 tile = tilePtr->copy(); 1105 s_dashedLineCache->insert(key, tilePtr); 1106 } 1107 else { 1108 tile = *tilePtr; 1109 } 1110 1111 QRect r = QRect(x1, y1, x2 - x1, y2 - y1); 1112 if (p->hasClipping()) { 1113 r &= p->clipRegion().boundingRect(); 1114 } 1115 1116 // Make sure we're drawing the pattern in the correct phase 1117 if (horizontal && r.left() > x1) { 1118 offset.rx() += (x1 - r.left()); 1119 } else if (!horizontal && r.top() > y1) { 1120 offset.ry() += (y1 - r.top()); 1121 } 1122 1123 p->drawTiledPixmap(r, tile, -offset); 1124 } else { 1125 const QRect bounding(x1, y1, x2 - x1, y2 - y1); 1126 QPainterPath path; 1127 if (s == BSBottom || s == BSTop) { //Horizontal 1128 if (offset.x() > 0) { 1129 offset.rx() -= onLen + offLen; 1130 } 1131 for (int x = x1 + offset.x(); x < x2; x += onLen + offLen) { 1132 const QRect r(x, y1, qMin(onLen, (x2 - x)), width); 1133 path.addRect(r & bounding); 1134 } 1135 } else { //Vertical 1136 if (offset.y() > 0) { 1137 offset.ry() -= onLen + offLen; 1138 } 1139 for (int y = y1 + offset.y(); y < y2; y += onLen + offLen) { 1140 const QRect r(x1, y, width, qMin(onLen, (y2 - y))); 1141 path.addRect(r & bounding); 1142 } 1143 } 1144 1145 p->fillPath(path, c); 1146 } 1147 break; 1148 } 1149 case DOUBLE: { 1150 int third = (width + 1) / 3; 1151 1152 if (adjbw1 == 0 && adjbw2 == 0) { 1153 p->setPen(Qt::NoPen); 1154 p->setBrush(c); 1155 switch (s) { 1156 case BSTop: 1157 case BSBottom: 1158 p->drawRect(x1, y1, x2 - x1, third); 1159 p->drawRect(x1, y2 - third, x2 - x1, third); 1160 break; 1161 case BSLeft: 1162 p->drawRect(x1, y1, third, y2 - y1); 1163 p->drawRect(x2 - third, y1, third, y2 - y1); 1164 break; 1165 case BSRight: 1166 p->drawRect(x1, y1, third, y2 - y1); 1167 p->drawRect(x2 - third, y1, third, y2 - y1); 1168 break; 1169 } 1170 } else { 1171 int adjbw1bigthird; 1172 if (adjbw1 > 0) { 1173 adjbw1bigthird = adjbw1 + 1; 1174 } else { 1175 adjbw1bigthird = adjbw1 - 1; 1176 } 1177 adjbw1bigthird /= 3; 1178 1179 int adjbw2bigthird; 1180 if (adjbw2 > 0) { 1181 adjbw2bigthird = adjbw2 + 1; 1182 } else { 1183 adjbw2bigthird = adjbw2 - 1; 1184 } 1185 adjbw2bigthird /= 3; 1186 1187 switch (s) { 1188 case BSTop: 1189 drawBorder(p, x1 + qMax((-adjbw1 * 2 + 1) / 3, 0), y1, x2 - qMax((-adjbw2 * 2 + 1) / 3, 0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1190 drawBorder(p, x1 + qMax((adjbw1 * 2 + 1) / 3, 0), y2 - third, x2 - qMax((adjbw2 * 2 + 1) / 3, 0), y2, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1191 break; 1192 case BSLeft: 1193 drawBorder(p, x1, y1 + qMax((-adjbw1 * 2 + 1) / 3, 0), x1 + third, y2 - qMax((-adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1194 drawBorder(p, x2 - third, y1 + qMax((adjbw1 * 2 + 1) / 3, 0), x2, y2 - qMax((adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1195 break; 1196 case BSBottom: 1197 drawBorder(p, x1 + qMax((adjbw1 * 2 + 1) / 3, 0), y1, x2 - qMax((adjbw2 * 2 + 1) / 3, 0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1198 drawBorder(p, x1 + qMax((-adjbw1 * 2 + 1) / 3, 0), y2 - third, x2 - qMax((-adjbw2 * 2 + 1) / 3, 0), y2, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1199 break; 1200 case BSRight: 1201 drawBorder(p, x1, y1 + qMax((adjbw1 * 2 + 1) / 3, 0), x1 + third, y2 - qMax((adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1202 drawBorder(p, x2 - third, y1 + qMax((-adjbw1 * 2 + 1) / 3, 0), x2, y2 - qMax((-adjbw2 * 2 + 1) / 3, 0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); 1203 break; 1204 default: 1205 break; 1206 } 1207 } 1208 break; 1209 } 1210 case RIDGE: 1211 case GROOVE: { 1212 EBorderStyle s1; 1213 EBorderStyle s2; 1214 if (style == GROOVE) { 1215 s1 = INSET; 1216 s2 = OUTSET; 1217 } else { 1218 s1 = OUTSET; 1219 s2 = INSET; 1220 } 1221 1222 int adjbw1bighalf; 1223 int adjbw2bighalf; 1224 if (adjbw1 > 0) { 1225 adjbw1bighalf = adjbw1 + 1; 1226 } else { 1227 adjbw1bighalf = adjbw1 - 1; 1228 } 1229 adjbw1bighalf /= 2; 1230 1231 if (adjbw2 > 0) { 1232 adjbw2bighalf = adjbw2 + 1; 1233 } else { 1234 adjbw2bighalf = adjbw2 - 1; 1235 } 1236 adjbw2bighalf /= 2; 1237 1238 switch (s) { 1239 case BSTop: 1240 drawBorder(p, x1 + qMax(-adjbw1, 0) / 2, y1, x2 - qMax(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); 1241 drawBorder(p, x1 + qMax(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - qMax(adjbw2 + 1, 0) / 2, y2, s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); 1242 break; 1243 case BSLeft: 1244 drawBorder(p, x1, y1 + qMax(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - qMax(-adjbw2, 0) / 2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); 1245 drawBorder(p, (x1 + x2 + 1) / 2, y1 + qMax(adjbw1 + 1, 0) / 2, x2, y2 - qMax(adjbw2 + 1, 0) / 2, s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); 1246 break; 1247 case BSBottom: 1248 drawBorder(p, x1 + qMax(adjbw1, 0) / 2, y1, x2 - qMax(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); 1249 drawBorder(p, x1 + qMax(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - qMax(-adjbw2 + 1, 0) / 2, y2, s, c, textcolor, s1, adjbw1 / 2, adjbw2 / 2); 1250 break; 1251 case BSRight: 1252 drawBorder(p, x1, y1 + qMax(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - qMax(adjbw2, 0) / 2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); 1253 drawBorder(p, (x1 + x2 + 1) / 2, y1 + qMax(-adjbw1 + 1, 0) / 2, x2, y2 - qMax(-adjbw2 + 1, 0) / 2, s, c, textcolor, s1, adjbw1 / 2, adjbw2 / 2); 1254 break; 1255 } 1256 break; 1257 } 1258 case INSET: 1259 case OUTSET: 1260 calc3DColor(c, (style == OUTSET && (s == BSBottom || s == BSRight)) || 1261 (style == INSET && (s == BSTop || s == BSLeft))); 1262 /* nobreak; */ 1263 case SOLID: 1264 p->setPen(Qt::NoPen); 1265 p->setBrush(c); 1266 Q_ASSERT(x2 >= x1); 1267 Q_ASSERT(y2 >= y1); 1268 if (adjbw1 == 0 && adjbw2 == 0) { 1269 p->drawRect(x1, y1, x2 - x1, y2 - y1); 1270 return; 1271 } 1272 QPolygon quad(4); 1273 switch (s) { 1274 case BSTop: 1275 quad.setPoints(4, 1276 x1 + qMax(-adjbw1, 0), y1, 1277 x1 + qMax(adjbw1, 0), y2, 1278 x2 - qMax(adjbw2, 0), y2, 1279 x2 - qMax(-adjbw2, 0), y1); 1280 break; 1281 case BSBottom: 1282 quad.setPoints(4, 1283 x1 + qMax(adjbw1, 0), y1, 1284 x1 + qMax(-adjbw1, 0), y2, 1285 x2 - qMax(-adjbw2, 0), y2, 1286 x2 - qMax(adjbw2, 0), y1); 1287 break; 1288 case BSLeft: 1289 quad.setPoints(4, 1290 x1, y1 + qMax(-adjbw1, 0), 1291 x1, y2 - qMax(-adjbw2, 0), 1292 x2, y2 - qMax(adjbw2, 0), 1293 x2, y1 + qMax(adjbw1, 0)); 1294 break; 1295 case BSRight: 1296 quad.setPoints(4, 1297 x1, y1 + qMax(adjbw1, 0), 1298 x1, y2 - qMax(adjbw2, 0), 1299 x2, y2 - qMax(-adjbw2, 0), 1300 x2, y1 + qMax(-adjbw1, 0)); 1301 break; 1302 } 1303 p->drawConvexPolygon(quad); 1304 break; 1305 } 1306 1307 if (invalidisInvert && p->compositionMode() == QPainter::CompositionMode_Difference) { 1308 p->setCompositionMode(QPainter::CompositionMode_SourceOver); 1309 } 1310 } 1311 1312 void RenderObject::calcBorderRadii(QPoint &topLeftRadii, QPoint &topRightRadii, QPoint &bottomLeftRadii, QPoint &bottomRightRadii, int w, int h) const 1313 { 1314 // CSS Backgrounds and Borders Module Level 3 (https://www.w3.org/TR/2014/CR-css3-background-20140909/), chapter 5.5: 1315 // "Corner curves must not overlap: When the sum of any two adjacent border radii exceeds the size of the border box, 1316 // UAs must proportionally reduce the used values of all border radii until none of them overlap. 1317 // The algorithm for reducing radii is as follows: ..." 1318 1319 const RenderStyle *s = style(); 1320 if (!s->hasBorderRadius()) { 1321 return; 1322 } 1323 1324 // Border radii Length is Fixed|Percent 1325 topLeftRadii.rx() = s->borderTopLeftRadius().horizontal.minWidth(w); 1326 topLeftRadii.ry() = s->borderTopLeftRadius().vertical.minWidth(h); 1327 topRightRadii.rx() = s->borderTopRightRadius().horizontal.minWidth(w); 1328 topRightRadii.ry() = s->borderTopRightRadius().vertical.minWidth(h); 1329 bottomLeftRadii.rx() = s->borderBottomLeftRadius().horizontal.minWidth(w); 1330 bottomLeftRadii.ry() = s->borderBottomLeftRadius().vertical.minWidth(h); 1331 bottomRightRadii.rx() = s->borderBottomRightRadius().horizontal.minWidth(w); 1332 bottomRightRadii.ry() = s->borderBottomRightRadius().vertical.minWidth(h); 1333 1334 // Adjust the border radii so they don't overlap when taking the size of the box into account. 1335 1336 const int horS = qMax(topLeftRadii.x() + topRightRadii.x(), bottomLeftRadii.x() + bottomRightRadii.x()); 1337 const int verS = qMax(topLeftRadii.y() + bottomLeftRadii.y(), topRightRadii.y() + bottomRightRadii.y()); 1338 1339 qreal f = 1.0; 1340 if (horS > 0) { 1341 f = qMin(f, w / qreal(horS)); 1342 } 1343 if (verS > 0) { 1344 f = qMin(f, h / qreal(verS)); 1345 } 1346 1347 if (f < 1.0) { 1348 topLeftRadii *= f; 1349 topRightRadii *= f; 1350 bottomLeftRadii *= f; 1351 bottomRightRadii *= f; 1352 } 1353 } 1354 1355 static QImage blendCornerImages(const QImage &image1, const QImage &image2) 1356 { 1357 QImage mask(image1.size(), QImage::Format_ARGB32_Premultiplied); 1358 QImage composite = image1; 1359 QImage temp = image2; 1360 1361 // Construct the mask image 1362 QConicalGradient gradient(mask.width() / 2, mask.height() / 2, 0); 1363 gradient.setColorAt(0.00, Qt::transparent); 1364 gradient.setColorAt(0.25, Qt::black); 1365 gradient.setColorAt(0.50, Qt::black); 1366 gradient.setColorAt(0.75, Qt::transparent); 1367 gradient.setColorAt(1.00, Qt::transparent); 1368 1369 QBrush gradientBrush = gradient; 1370 1371 if (mask.width() != mask.height()) { 1372 int min = qMin(mask.width(), mask.height()); 1373 QTransform xform; 1374 xform.translate(mask.width() / 2, mask.height() / 2); 1375 xform.scale(min / mask.width(), min / mask.height()); 1376 gradientBrush.setTransform(xform); 1377 } 1378 1379 QPainter p; 1380 p.begin(&mask); 1381 p.setCompositionMode(QPainter::CompositionMode_Source); 1382 p.fillRect(mask.rect(), gradientBrush); 1383 p.end(); 1384 1385 p.begin(&temp); 1386 p.setCompositionMode(QPainter::CompositionMode_DestinationIn); 1387 p.drawImage(0, 0, mask); 1388 p.end(); 1389 1390 p.begin(&composite); 1391 p.setCompositionMode(QPainter::CompositionMode_DestinationOut); 1392 p.drawImage(0, 0, mask); 1393 p.setCompositionMode(QPainter::CompositionMode_SourceOver); 1394 p.drawImage(0, 0, temp); 1395 p.end(); 1396 1397 return composite; 1398 } 1399 1400 static QBrush cornerGradient(int cx, int cy, const QPoint &radius, int angleStart, int angleSpan, 1401 const QColor &startColor, const QColor &finalColor) 1402 { 1403 QConicalGradient g(0, 0, angleStart); 1404 g.setColorAt(0, startColor); 1405 g.setColorAt(angleSpan / 360.0, finalColor); 1406 1407 QBrush brush(g); 1408 1409 QTransform xform; 1410 xform.translate(cx, cy); 1411 1412 if (radius.x() < radius.y()) { 1413 xform.scale(radius.x() / radius.y(), 1); 1414 } else if (radius.y() < radius.x()) { 1415 xform.scale(1, radius.y() / radius.x()); 1416 } 1417 1418 brush.setTransform(xform); 1419 return brush; 1420 } 1421 1422 void RenderObject::drawBorderArc(QPainter *p, int x, int y, float horThickness, float vertThickness, 1423 const QPoint &radius, int angleStart, int angleSpan, const QBrush &brush, 1424 const QColor &textColor, EBorderStyle style, qreal *nextDashOffset) const 1425 { 1426 QColor c = brush.color(); 1427 if (!c.isValid()) { 1428 if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) { 1429 c = Qt::white; 1430 } else { 1431 c = textColor; 1432 } 1433 } 1434 1435 QColor light = c; 1436 QColor dark = c; 1437 calc3DColor(light, false); 1438 calc3DColor(dark, true); 1439 1440 if (style == DOUBLE && horThickness < 3 && vertThickness < 3) { 1441 style = SOLID; 1442 } 1443 1444 if (nextDashOffset && style != DOTTED && style != DASHED) { 1445 *nextDashOffset = 0; 1446 } 1447 1448 p->save(); 1449 p->setRenderHint(QPainter::Antialiasing); 1450 1451 switch (style) { 1452 case BNATIVE: 1453 case BNONE: 1454 case BHIDDEN: { 1455 // Should not happen 1456 break; 1457 } 1458 1459 case SOLID: { 1460 const QRect outerRect = QRect(x - radius.x(), y - radius.y(), radius.x() * 2, radius.y() * 2); 1461 const QRect innerRect = outerRect.adjusted(horThickness, vertThickness, -horThickness, -vertThickness); 1462 QPainterPath path; 1463 path.arcMoveTo(outerRect, angleStart); 1464 path.arcTo(outerRect, angleStart, angleSpan); 1465 if (innerRect.isValid()) { 1466 path.arcTo(innerRect, angleStart + angleSpan, -angleSpan); 1467 } else { 1468 path.lineTo(x, y); 1469 } 1470 path.closeSubpath(); 1471 p->fillPath(path, brush); 1472 break; 1473 } 1474 1475 case DOUBLE: { 1476 const qreal hw = (horThickness + 1) / 3; 1477 const qreal vw = (vertThickness + 1) / 3; 1478 1479 QPoint br(radius.x() - hw * 2 + 1, radius.y() - vw * 2 + 1); 1480 1481 drawBorderArc(p, x, y, hw, vw, radius, angleStart, angleSpan, brush, textColor, SOLID); 1482 drawBorderArc(p, x, y, hw, vw, br, angleStart, angleSpan, brush, textColor, SOLID); 1483 break; 1484 } 1485 1486 case INSET: 1487 case OUTSET: { 1488 QImage image1(radius.x() * 2, radius.y() * 2, QImage::Format_ARGB32_Premultiplied); 1489 image1.fill(0); 1490 1491 QImage image2 = image1; 1492 1493 const QColor c1 = style == OUTSET ? dark : light; 1494 const QColor c2 = style == OUTSET ? light : dark; 1495 1496 QPainter p2; 1497 p2.begin(&image1); 1498 drawBorderArc(&p2, radius.x(), radius.y(), horThickness, vertThickness, 1499 radius, angleStart, angleSpan, c1, textColor, SOLID); 1500 p2.end(); 1501 1502 p2.begin(&image2); 1503 drawBorderArc(&p2, radius.x(), radius.y(), horThickness, vertThickness, 1504 radius, angleStart, angleSpan, c2, textColor, SOLID); 1505 p2.end(); 1506 1507 p->drawImage(x - radius.x(), y - radius.y(), blendCornerImages(image1, image2)); 1508 break; 1509 } 1510 1511 case RIDGE: 1512 case GROOVE: { 1513 QImage image1(radius.x() * 2, radius.y() * 2, QImage::Format_ARGB32_Premultiplied); 1514 image1.fill(0); 1515 1516 QImage image2 = image1; 1517 1518 const QColor c1 = style == RIDGE ? dark : light; 1519 const QColor c2 = style == RIDGE ? light : dark; 1520 1521 const qreal hw = horThickness / 2; 1522 const qreal vw = vertThickness / 2; 1523 int cx = radius.x(); 1524 int cy = radius.y(); 1525 1526 QPoint innerRadius(radius.x() - hw, radius.y() - vw); 1527 1528 QPainter p2; 1529 p2.begin(&image1); 1530 drawBorderArc(&p2, cx, cy, hw, vw, radius, angleStart, angleSpan, c1, textColor, SOLID); 1531 drawBorderArc(&p2, cx, cy, hw, vw, innerRadius, angleStart, angleSpan, c2, textColor, SOLID); 1532 p2.end(); 1533 1534 p2.begin(&image2); 1535 drawBorderArc(&p2, cx, cy, hw, vw, radius, angleStart, angleSpan, c2, textColor, SOLID); 1536 drawBorderArc(&p2, cx, cy, hw, vw, innerRadius, angleStart, angleSpan, c1, textColor, SOLID); 1537 p2.end(); 1538 1539 p->drawImage(x - radius.x(), y - radius.y(), blendCornerImages(image1, image2)); 1540 break; 1541 } 1542 1543 case DOTTED: 1544 case DASHED: { 1545 const QRectF rect = QRectF(x - radius.x(), y - radius.y(), radius.x() * 2, radius.y() * 2); 1546 int width; 1547 1548 // Figure out which border we're starting from 1549 angleStart = angleStart % 360; 1550 if (angleStart < 0) { 1551 angleStart += 360; 1552 } 1553 1554 if ((angleStart > 45 && angleStart <= 135) || (angleStart > 225 && angleStart <= 315)) { 1555 width = vertThickness; 1556 } else { 1557 width = horThickness; 1558 } 1559 1560 int onLen = width; 1561 int offLen = width; 1562 1563 if (style == DASHED) { 1564 if (width == 1) { 1565 onLen = 3; 1566 offLen = 3; 1567 } else { 1568 onLen = width * 3; 1569 offLen = width; 1570 } 1571 } 1572 1573 BorderArcStroker stroker; 1574 stroker.setArc(rect, angleStart, angleSpan); 1575 stroker.setPenWidth(horThickness, vertThickness); 1576 stroker.setDashPattern(onLen, offLen); 1577 stroker.setDashOffset(*nextDashOffset); 1578 1579 const QPainterPath path = stroker.createStroke(nextDashOffset); 1580 p->fillPath(path, brush); 1581 } 1582 } 1583 1584 p->restore(); 1585 } 1586 1587 void RenderObject::paintBorder(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle *style, bool begin, bool end) 1588 { 1589 const QColor &tc = style->borderTopColor(); 1590 const QColor &bc = style->borderBottomColor(); 1591 const QColor &lc = style->borderLeftColor(); 1592 const QColor &rc = style->borderRightColor(); 1593 1594 bool tt = style->borderTopIsTransparent(); 1595 bool bt = style->borderBottomIsTransparent(); 1596 bool rt = style->borderRightIsTransparent(); 1597 bool lt = style->borderLeftIsTransparent(); 1598 1599 EBorderStyle ts = style->borderTopStyle(); 1600 EBorderStyle bs = style->borderBottomStyle(); 1601 EBorderStyle ls = style->borderLeftStyle(); 1602 EBorderStyle rs = style->borderRightStyle(); 1603 1604 bool render_t = ts > BHIDDEN && !tt; 1605 bool render_l = ls > BHIDDEN && begin && !lt; 1606 bool render_r = rs > BHIDDEN && end && !rt; 1607 bool render_b = bs > BHIDDEN && !bt; 1608 1609 QPoint topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii; 1610 calcBorderRadii(topLeftRadii, topRightRadii, bottomLeftRadii, bottomRightRadii, w, h); 1611 1612 bool upperLeftBorderStylesMatch = render_l && (ts == ls) && (tc == lc); 1613 bool upperRightBorderStylesMatch = render_r && (ts == rs) && (tc == rc); 1614 bool lowerLeftBorderStylesMatch = render_l && (bs == ls) && (bc == lc); 1615 bool lowerRightBorderStylesMatch = render_r && (bs == rs) && (bc == rc); 1616 1617 // We do a gradient transition for dotted, dashed, solid and double lines 1618 // when the styles match but the colors differ. 1619 bool upperLeftGradient = render_t && render_l && ts == ls && tc != lc && ts > OUTSET; 1620 bool upperRightGradient = render_t && render_r && ts == rs && tc != rc && ts > OUTSET; 1621 bool lowerLeftGradient = render_b && render_l && bs == ls && bc != lc && bs > OUTSET; 1622 bool lowerRightGradient = render_b && render_r && bs == rs && bc != rc && bs > OUTSET; 1623 1624 qreal nextDashOffset = 0; 1625 1626 // Draw the borders counter-clockwise starting with the upper right corner 1627 if (render_t) { 1628 bool ignore_left = (topLeftRadii.x() > 0) || 1629 ((tc == lc) && (tt == lt) && 1630 (ts >= OUTSET) && 1631 (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); 1632 1633 bool ignore_right = (topRightRadii.x() > 0) || 1634 ((tc == rc) && (tt == rt) && 1635 (ts >= OUTSET) && 1636 (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); 1637 1638 int x = _tx + topLeftRadii.x(); 1639 int x2 = _tx + w - topRightRadii.x(); 1640 1641 if (!topRightRadii.isNull()) { 1642 int x = _tx + w - topRightRadii.x(); 1643 int y = _ty + topRightRadii.y(); 1644 int startAngle, span; 1645 1646 if (upperRightBorderStylesMatch || upperRightGradient) { 1647 startAngle = 0; 1648 span = 90; 1649 } else { 1650 startAngle = 45; 1651 span = 45; 1652 } 1653 1654 const QBrush brush = upperRightGradient ? 1655 cornerGradient(x, y, topRightRadii, startAngle, span, rc, tc) : tc; 1656 1657 // Draw the upper right arc 1658 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderTopWidth(), 1659 topRightRadii, startAngle, span, brush, style->color(), ts, &nextDashOffset); 1660 } 1661 1662 drawBorder(p, x, _ty, x2, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, 1663 ignore_left ? 0 : style->borderLeftWidth(), 1664 ignore_right ? 0 : style->borderRightWidth(), false, &nextDashOffset); 1665 1666 if (!topLeftRadii.isNull()) { 1667 int x = _tx + topLeftRadii.x(); 1668 int y = _ty + topLeftRadii.y(); 1669 int startAngle = 90; 1670 int span = (upperLeftBorderStylesMatch || upperLeftGradient) ? 90 : 45; 1671 const QBrush brush = upperLeftGradient ? 1672 cornerGradient(x, y, topLeftRadii, startAngle, span, tc, lc) : tc; 1673 1674 // Draw the upper left arc 1675 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderTopWidth(), 1676 topLeftRadii, startAngle, span, brush, style->color(), ts, &nextDashOffset); 1677 } else if (ls == DASHED || ls == DOTTED) { 1678 nextDashOffset = 0; // Reset the offset to avoid partially overlapping dashes 1679 } 1680 } 1681 1682 if (render_l) { 1683 bool ignore_top = (topLeftRadii.y() > 0) || 1684 ((tc == lc) && (tt == lt) && 1685 (ls >= OUTSET) && 1686 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); 1687 1688 bool ignore_bottom = (bottomLeftRadii.y() > 0) || 1689 ((bc == lc) && (bt == lt) && 1690 (ls >= OUTSET) && 1691 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); 1692 1693 int y = _ty + topLeftRadii.y(); 1694 int y2 = _ty + h - bottomLeftRadii.y(); 1695 1696 if (!upperLeftBorderStylesMatch && !upperLeftGradient && !topLeftRadii.isNull()) { 1697 int x = _tx + topLeftRadii.x(); 1698 int y = _ty + topLeftRadii.y(); 1699 int startAngle = 135; 1700 int span = 45; 1701 1702 // Draw the upper left arc 1703 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderTopWidth(), 1704 topLeftRadii, startAngle, span, lc, style->color(), ls, &nextDashOffset); 1705 } 1706 1707 drawBorder(p, _tx, y, _tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, 1708 ignore_top ? 0 : style->borderTopWidth(), 1709 ignore_bottom ? 0 : style->borderBottomWidth(), false, &nextDashOffset); 1710 1711 if (!lowerLeftBorderStylesMatch && !lowerLeftGradient && !bottomLeftRadii.isNull()) { 1712 int x = _tx + bottomLeftRadii.x(); 1713 int y = _ty + h - bottomLeftRadii.y(); 1714 int startAngle = 180; 1715 int span = 45; 1716 1717 // Draw the bottom left arc 1718 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderBottomWidth(), 1719 bottomLeftRadii, startAngle, span, lc, style->color(), ls, &nextDashOffset); 1720 } 1721 1722 // Reset the offset to avoid partially overlapping dashes 1723 if (bottomLeftRadii.isNull() && (bs == DASHED || bs == DOTTED)) { 1724 nextDashOffset = 0; 1725 } 1726 } 1727 1728 if (render_b) { 1729 bool ignore_left = (bottomLeftRadii.x() > 0) || 1730 ((bc == lc) && (bt == lt) && 1731 (bs >= OUTSET) && 1732 (ls == DOTTED || ls == DASHED || ls == SOLID || ls == INSET)); 1733 1734 bool ignore_right = (bottomRightRadii.x() > 0) || 1735 ((bc == rc) && (bt == rt) && 1736 (bs >= OUTSET) && 1737 (rs == DOTTED || rs == DASHED || rs == SOLID || rs == OUTSET)); 1738 1739 int x = _tx + bottomLeftRadii.x(); 1740 int x2 = _tx + w - bottomRightRadii.x(); 1741 1742 if (!bottomLeftRadii.isNull()) { 1743 int x = _tx + bottomLeftRadii.x(); 1744 int y = _ty + h - bottomLeftRadii.y(); 1745 int startAngle, span; 1746 1747 if (lowerLeftBorderStylesMatch || lowerLeftGradient) { 1748 startAngle = 180; 1749 span = 90; 1750 } else { 1751 startAngle = 225; 1752 span = 45; 1753 } 1754 1755 const QBrush brush = lowerLeftGradient ? 1756 cornerGradient(x, y, bottomLeftRadii, startAngle, span, lc, bc) : bc; 1757 1758 // Draw the bottom left arc 1759 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderBottomWidth(), 1760 bottomLeftRadii, startAngle, span, brush, style->color(), bs, &nextDashOffset); 1761 } 1762 1763 drawBorder(p, x, _ty + h - style->borderBottomWidth(), x2, _ty + h, BSBottom, bc, style->color(), bs, 1764 ignore_left ? 0 : style->borderLeftWidth(), 1765 ignore_right ? 0 : style->borderRightWidth(), false, &nextDashOffset); 1766 1767 if (!bottomRightRadii.isNull()) { 1768 int x = _tx + w - bottomRightRadii.x(); 1769 int y = _ty + h - bottomRightRadii.y(); 1770 int startAngle = 270; 1771 int span = (lowerRightBorderStylesMatch || lowerRightGradient) ? 90 : 45; 1772 const QBrush brush = lowerRightGradient ? 1773 cornerGradient(x, y, bottomRightRadii, startAngle, span, bc, rc) : bc; 1774 1775 // Draw the bottom right arc 1776 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderBottomWidth(), 1777 bottomRightRadii, startAngle, span, brush, style->color(), bs, &nextDashOffset); 1778 } else if (rs == DASHED || rs == DOTTED) { 1779 nextDashOffset = 0; // Reset the offset to avoid partially overlapping dashes 1780 } 1781 } 1782 1783 if (render_r) { 1784 bool ignore_top = (topRightRadii.y() > 0) || 1785 ((tc == rc) && (tt == rt) && 1786 (rs >= DOTTED || rs == INSET) && 1787 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); 1788 1789 bool ignore_bottom = (bottomRightRadii.y() > 0) || 1790 ((bc == rc) && (bt == rt) && 1791 (rs >= DOTTED || rs == INSET) && 1792 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); 1793 1794 int y = _ty + topRightRadii.y(); 1795 int y2 = _ty + h - bottomRightRadii.y(); 1796 1797 if (!lowerRightBorderStylesMatch && !lowerRightGradient && !bottomRightRadii.isNull()) { 1798 int x = _tx + w - bottomRightRadii.x(); 1799 int y = _ty + h - bottomRightRadii.y(); 1800 int startAngle = 315; 1801 int span = 45; 1802 1803 // Draw the bottom right arc 1804 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderBottomWidth(), 1805 bottomRightRadii, startAngle, span, rc, style->color(), rs, &nextDashOffset); 1806 } 1807 1808 drawBorder(p, _tx + w - style->borderRightWidth(), y, _tx + w, y2, BSRight, rc, style->color(), rs, 1809 ignore_top ? 0 : style->borderTopWidth(), 1810 ignore_bottom ? 0 : style->borderBottomWidth(), false, &nextDashOffset); 1811 1812 if (!upperRightBorderStylesMatch && !upperRightGradient && !topRightRadii.isNull()) { 1813 int x = _tx + w - topRightRadii.x(); 1814 int y = _ty + topRightRadii.y(); 1815 int startAngle = 0; 1816 int span = 45; 1817 1818 // Draw the upper right arc 1819 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderTopWidth(), 1820 topRightRadii, startAngle, span, rc, style->color(), rs, &nextDashOffset); 1821 } 1822 } 1823 } 1824 1825 void RenderObject::paintOutline(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle *style) 1826 { 1827 int ow = style->outlineWidth(); 1828 if (!ow) { 1829 return; 1830 } 1831 1832 const QColor &oc = style->outlineColor(); 1833 EBorderStyle os = style->outlineStyle(); 1834 int offset = style->outlineOffset(); 1835 1836 #ifdef APPLE_CHANGES 1837 if (style->outlineStyleIsAuto()) { 1838 p->initFocusRing(ow, offset, oc); 1839 addFocusRingRects(p, _tx, _ty); 1840 p->drawFocusRing(); 1841 p->clearFocusRing(); 1842 return; 1843 } 1844 #endif 1845 1846 _tx -= offset; 1847 _ty -= offset; 1848 w += 2 * offset; 1849 h += 2 * offset; 1850 1851 drawBorder(p, _tx - ow, _ty - ow, _tx, _ty + h + ow, BSLeft, 1852 QColor(oc), style->color(), 1853 os, ow, ow, true); 1854 1855 drawBorder(p, _tx - ow, _ty - ow, _tx + w + ow, _ty, BSTop, 1856 QColor(oc), style->color(), 1857 os, ow, ow, true); 1858 1859 drawBorder(p, _tx + w, _ty - ow, _tx + w + ow, _ty + h + ow, BSRight, 1860 QColor(oc), style->color(), 1861 os, ow, ow, true); 1862 1863 drawBorder(p, _tx - ow, _ty + h, _tx + w + ow, _ty + h + ow, BSBottom, 1864 QColor(oc), style->color(), 1865 os, ow, ow, true); 1866 1867 } 1868 1869 void RenderObject::paint(PaintInfo &, int /*tx*/, int /*ty*/) 1870 { 1871 } 1872 1873 void RenderObject::repaintRectangle(int x, int y, int w, int h, Priority p, bool f) 1874 { 1875 if (parent()) { 1876 parent()->repaintRectangle(x, y, w, h, p, f); 1877 } 1878 } 1879 1880 #ifdef ENABLE_DUMP 1881 1882 QString RenderObject::information() const 1883 { 1884 QString str; 1885 int x; int y; 1886 absolutePosition(x, y); 1887 x += inlineXPos(); 1888 y += inlineYPos(); 1889 QTextStream ts(&str, QIODevice::WriteOnly); 1890 ts << renderName() 1891 << "(" << (style() ? style()->refCount() : 0) << ")" 1892 << ": " << (void *)this << " "; 1893 ts << "{" << x << " " << y << "} "; 1894 if (isInline()) { 1895 ts << "il "; 1896 } 1897 if (childrenInline()) { 1898 ts << "ci "; 1899 } 1900 if (isFloating()) { 1901 ts << "fl "; 1902 } 1903 if (isAnonymous()) { 1904 ts << "an "; 1905 } 1906 if (isRelPositioned()) { 1907 ts << "rp "; 1908 } 1909 if (isPositioned()) { 1910 ts << "ps "; 1911 } 1912 if (isReplaced()) { 1913 ts << "rp "; 1914 } 1915 if (needsLayout()) { 1916 ts << "nl "; 1917 } 1918 if (minMaxKnown()) { 1919 ts << "mmk "; 1920 } 1921 if (m_recalcMinMax) { 1922 ts << "rmm "; 1923 } 1924 if (mouseInside()) { 1925 ts << "mi "; 1926 } 1927 if (style() && style()->zIndex()) { 1928 ts << "zI: " << style()->zIndex(); 1929 } 1930 if (style() && style()->hasAutoZIndex()) { 1931 ts << "zI: auto "; 1932 } 1933 if (element()) { 1934 if (element()->active()) { 1935 ts << "act "; 1936 } 1937 if (element()->hasAnchor()) { 1938 ts << "anchor "; 1939 } 1940 if (element()->focused()) { 1941 ts << "focus "; 1942 } 1943 ts << " <" << LocalName::fromId(localNamePart(element()->id())).toString().string() << ">"; 1944 1945 } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) { 1946 ts << " <" << LocalName::fromId(localNamePart(node()->id())).toString().string(); 1947 QString pseudo; 1948 switch (style()->styleType()) { 1949 case RenderStyle::FIRST_LETTER: 1950 pseudo = ":first-letter"; break; 1951 case RenderStyle::BEFORE: 1952 pseudo = ":before"; break; 1953 case RenderStyle::AFTER: 1954 pseudo = ":after"; break; 1955 default: 1956 pseudo = ":pseudo-element"; 1957 } 1958 ts << pseudo; 1959 ts << ">"; 1960 } 1961 ts << " (" << xPos() << "," << yPos() << "," << width() << "," << height() << ")" 1962 << " [" << minWidth() << "-" << maxWidth() << "]" 1963 << " { mT: " << marginTop() << " qT: " << isTopMarginQuirk() 1964 << " mB: " << marginBottom() << " qB: " << isBottomMarginQuirk() 1965 << "}" 1966 << (isTableCell() ? 1967 (QLatin1String(" [r=") + 1968 QString::number(static_cast<const RenderTableCell *>(this)->row()) + 1969 QLatin1String(" c=") + 1970 QString::number(static_cast<const RenderTableCell *>(this)->col()) + 1971 QLatin1String(" rs=") + 1972 QString::number(static_cast<const RenderTableCell *>(this)->rowSpan()) + 1973 QLatin1String(" cs=") + 1974 QString::number(static_cast<const RenderTableCell *>(this)->colSpan()) + 1975 QLatin1String("]")) : QString()); 1976 if (layer()) { 1977 ts << " layer=" << layer(); 1978 } 1979 if (continuation()) { 1980 ts << " continuation=" << continuation(); 1981 } 1982 if (isText()) { 1983 ts << " \"" << QString::fromRawData(static_cast<const RenderText *>(this)->text(), qMin(static_cast<const RenderText *>(this)->length(), 10u)) << "\""; 1984 } 1985 return str; 1986 } 1987 1988 void RenderObject::printTree(int indent) const 1989 { 1990 QString ind; 1991 ind.fill(' ', indent); 1992 1993 // qCDebug(KHTML_LOG) << (ind + information()); 1994 1995 RenderObject *child = firstChild(); 1996 while (child != nullptr) { 1997 child->printTree(indent + 2); 1998 child = child->nextSibling(); 1999 } 2000 } 2001 2002 static QTextStream &operator<<(QTextStream &ts, const QRect &r) 2003 { 2004 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); 2005 } 2006 2007 //A bit like getTagName, but handles XML, too. 2008 static QString lookupTagName(NodeImpl *node) 2009 { 2010 return LocalName::fromId(node->id()).toString().string(); 2011 } 2012 2013 void RenderObject::dump(QTextStream &ts, const QString &ind) const 2014 { 2015 if (!layer()) { 2016 ts << endl; 2017 } 2018 2019 ts << ind << renderName(); 2020 2021 if (style() && style()->zIndex()) { 2022 ts << " zI: " << style()->zIndex(); 2023 } 2024 2025 if (element()) { 2026 QString tagName(lookupTagName(element())); 2027 if (!tagName.isEmpty()) { 2028 ts << " {" << tagName << "}"; 2029 } 2030 } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) { 2031 QString pseudo; 2032 QString tagName(lookupTagName(node())); 2033 switch (style()->styleType()) { 2034 case RenderStyle::FIRST_LETTER: 2035 pseudo = ":first-letter"; break; 2036 case RenderStyle::BEFORE: 2037 pseudo = ":before"; break; 2038 case RenderStyle::AFTER: 2039 pseudo = ":after"; break; 2040 default: 2041 pseudo = ":pseudo-element"; 2042 } 2043 ts << " {" << tagName << pseudo << "}"; 2044 } 2045 2046 QRect r(xPos(), yPos(), width(), height()); 2047 ts << " " << r; 2048 2049 if (parent()) { 2050 ts << style()->createDiff(*parent()->style()); 2051 } 2052 2053 if (isAnonymous()) { 2054 ts << " anonymousBox"; 2055 } 2056 if (isFloating()) { 2057 ts << " floating"; 2058 } 2059 if (isPositioned()) { 2060 ts << " positioned"; 2061 } 2062 if (isRelPositioned()) { 2063 ts << " relPositioned"; 2064 } 2065 if (isText()) { 2066 ts << " text"; 2067 } 2068 if (isInline()) { 2069 ts << " inline"; 2070 } 2071 if (isReplaced()) { 2072 ts << " replaced"; 2073 } 2074 if (shouldPaintBackgroundOrBorder()) { 2075 ts << " paintBackground"; 2076 } 2077 if (needsLayout()) { 2078 ts << " needsLayout"; 2079 } 2080 if (minMaxKnown()) { 2081 ts << " minMaxKnown"; 2082 } 2083 if (hasFirstLine()) { 2084 ts << " hasFirstLine"; 2085 } 2086 if (afterPageBreak()) { 2087 ts << " afterPageBreak"; 2088 } 2089 } 2090 2091 void RenderObject::printLineBoxTree() const 2092 { 2093 RenderObject *child = firstChild(); 2094 for (; child; child = child->nextSibling()) { 2095 child->printLineBoxTree(); 2096 } 2097 if (isRenderBlock()) { 2098 const RenderBlock *block = static_cast<const RenderBlock *>(this); 2099 RootInlineBox *rootBox = block->firstRootBox(); 2100 for (; rootBox; rootBox = rootBox->nextRootBox()) { 2101 rootBox->printTree(); 2102 } 2103 } 2104 } 2105 #endif 2106 2107 bool RenderObject::shouldSelect() const 2108 { 2109 #if 0 // ### merge 2110 const RenderObject *curr = this; 2111 DOM::NodeImpl *node = 0; 2112 bool forcedOn = false; 2113 2114 while (curr) { 2115 if (curr->style()->userSelect() == SELECT_TEXT) { 2116 forcedOn = true; 2117 } 2118 if (!forcedOn && curr->style()->userSelect() == SELECT_NONE) { 2119 return false; 2120 } 2121 2122 if (!node) { 2123 node = curr->element(); 2124 } 2125 curr = curr->parent(); 2126 } 2127 2128 // somewhere up the render tree there must be an element! 2129 assert(node); 2130 2131 return node->dispatchHTMLEvent(DOM::EventImpl::SELECTSTART_EVENT, true, true); 2132 #else 2133 return true; 2134 #endif 2135 } 2136 2137 void RenderObject::selectionStartEnd(int &spos, int &epos) 2138 { 2139 if (parent()) { 2140 parent()->selectionStartEnd(spos, epos); 2141 } 2142 } 2143 2144 void RenderObject::setStyle(RenderStyle *style) 2145 { 2146 if (m_style == style) { 2147 return; 2148 } 2149 2150 RenderStyle::Diff d = m_style ? m_style->diff(style) : RenderStyle::Layout; 2151 //qDebug("m_style: %p new style, diff=%d", m_style, d); 2152 2153 Priority pri = NormalPriority; 2154 if (m_style) { 2155 pri = HighPriority; 2156 if (d >= RenderStyle::Visible && !isText() && m_parent && 2157 (d == RenderStyle::Position || 2158 m_style->outlineWidth() > style->outlineWidth() || 2159 (!m_style->hidesOverflow() && style->hidesOverflow()) || 2160 (m_style->hasClip() && !(m_style->clip() == style->clip())))) { 2161 // schedule a repaint with the old style 2162 if (layer() && !isInlineFlow()) { 2163 layer()->repaint(pri); 2164 } else { 2165 repaint(pri); 2166 } 2167 } 2168 2169 if ((isFloating() && m_style->floating() != style->floating()) || 2170 (isPositioned() && m_style->position() != style->position() && 2171 style->position() != PABSOLUTE && style->position() != PFIXED)) { 2172 removeFromObjectLists(); 2173 } 2174 2175 if (layer()) { 2176 if ((m_style->hasAutoZIndex() != style->hasAutoZIndex() || 2177 m_style->zIndex() != style->zIndex() || 2178 m_style->visibility() != style->visibility())) { 2179 layer()->stackingContext()->dirtyZOrderLists(); 2180 layer()->dirtyZOrderLists(); 2181 } 2182 // keep layer hierarchy visibility bits up to date if visibility changes 2183 if (m_style->visibility() != style->visibility()) { 2184 RenderLayer *l = enclosingLayer(); 2185 if (style->visibility() == VISIBLE && l) { 2186 l->setHasVisibleContent(true); 2187 } else if (l && l->hasVisibleContent() && 2188 (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { 2189 l->dirtyVisibleContentStatus(); 2190 } 2191 } 2192 } 2193 2194 // reset style flags 2195 m_floating = false; 2196 m_positioned = false; 2197 m_relPositioned = false; 2198 m_paintBackground = false; 2199 m_hasOverflowClip = false; 2200 } 2201 2202 // only honor z-index for non-static objects and objects with opacity 2203 if (style->position() == PSTATIC && style->opacity() == 1.0f) { 2204 style->setHasAutoZIndex(); 2205 } 2206 // force establishment of a stacking context by transparent objects, as those define 2207 // the bounds of an atomically painted region. 2208 if (style->hasAutoZIndex() && (isRoot() || style->opacity() < 1.0f)) { 2209 style->setZIndex(0); 2210 } 2211 2212 if (d > RenderStyle::Position && 2213 (style->hasFixedBackgroundImage() != (m_style && m_style->hasFixedBackgroundImage()) 2214 || (style->position() == PFIXED) != (m_style && (m_style->position() == PFIXED))) 2215 && canvas() && canvas()->view()) { 2216 // some sort of fixed object is added or removed. Let's find out more and report to the canvas, 2217 // so that it does some bookkeeping and optimizes the view's background display mode accordingly. 2218 bool fixedBG = style->hasFixedBackgroundImage(); 2219 bool oldFixedBG = m_style && m_style->hasFixedBackgroundImage(); 2220 bool fixedPos = (style->position() == PFIXED); 2221 bool oldFixedPos = m_style && (m_style->position() == PFIXED); 2222 if (fixedBG != oldFixedBG) { 2223 if (fixedBG) { 2224 canvas()->addStaticObject(this); 2225 } else { 2226 canvas()->removeStaticObject(this); 2227 } 2228 } 2229 if (fixedPos != oldFixedPos) { 2230 if (fixedPos) { 2231 canvas()->addStaticObject(this, true /*positioned*/); 2232 } else { 2233 canvas()->removeStaticObject(this, true); 2234 } 2235 } 2236 } 2237 2238 RenderStyle *oldStyle = m_style; 2239 m_style = style; 2240 2241 updateBackgroundImages(oldStyle); 2242 2243 m_style->ref(); 2244 2245 if (oldStyle) { 2246 oldStyle->deref(); 2247 } 2248 2249 setShouldPaintBackgroundOrBorder(m_style->hasBorder() || m_style->hasBackground()); 2250 2251 m_hasFirstLine = (style->getPseudoStyle(RenderStyle::FIRST_LINE) != nullptr); 2252 if (m_parent) { 2253 if (d == RenderStyle::Position && !attemptDirectLayerTranslation()) { 2254 d = RenderStyle::Layout; 2255 } 2256 2257 if (d > RenderStyle::Position) { 2258 // we must perform a full layout 2259 if (!isText() && d == RenderStyle::CbLayout) { 2260 dirtyFormattingContext(true); 2261 } 2262 setNeedsLayoutAndMinMaxRecalc(); 2263 } else if (!isText() && d >= RenderStyle::Visible) { 2264 // a repaint is enough 2265 if (layer()) { 2266 if (canvas() && canvas()->needsWidgetMasks()) { 2267 // update our widget masks 2268 RenderLayer *p, *d = nullptr; 2269 for (p = layer()->parent(); p; p = p->parent()) 2270 if (p->hasOverlaidWidgets()) { 2271 d = p; 2272 } 2273 if (d) { // deepest 2274 d->updateWidgetMasks(canvas()->layer()); 2275 } 2276 } 2277 } 2278 if (layer() && !isInlineFlow()) { 2279 layer()->repaint(pri); 2280 } else { 2281 repaint(pri); 2282 } 2283 } 2284 } 2285 } 2286 2287 bool RenderObject::attemptDirectLayerTranslation() 2288 { 2289 // When the difference between two successive styles is only 'Position' 2290 // we may attempt to save a layout by directly updating the object position. 2291 2292 KHTMLAssert(m_style->position() != PSTATIC); 2293 if (!layer()) { 2294 return false; 2295 } 2296 setInline(m_style->isDisplayInlineType()); 2297 setPositioned(m_style->position() != PRELATIVE); 2298 setRelPositioned(m_style->position() == PRELATIVE); 2299 int oldXPos = xPos(); 2300 int oldYPos = yPos(); 2301 int oldWidth = width(); 2302 int oldHeight = height(); 2303 calcWidth(); 2304 calcHeight(); 2305 if (oldWidth != width() || oldHeight != height()) { 2306 // implicit size change or overconstrained dimensions: 2307 // we'll need a layout. 2308 setWidth(oldWidth); 2309 setHeight(oldHeight); 2310 // qCDebug(KHTML_LOG) << "Layer translation failed for " << information(); 2311 return false; 2312 } 2313 layer()->updateLayerPosition(); 2314 if (m_style->position() != PFIXED) { 2315 bool needsDocSizeUpdate = true; 2316 RenderObject *cb = container(); 2317 while (cb) { 2318 if (cb->hasOverflowClip() && cb->layer()) { 2319 cb->layer()->checkScrollbarsAfterLayout(); 2320 needsDocSizeUpdate = false; 2321 break; 2322 } 2323 cb = cb->container(); 2324 } 2325 if (needsDocSizeUpdate && canvas()) { 2326 bool posXOffset = (xPos() - oldXPos >= 0); 2327 bool posYOffset = (yPos() - oldYPos >= 0); 2328 canvas()->updateDocSizeAfterLayerTranslation(this, posXOffset, posYOffset); 2329 } 2330 } 2331 // success 2332 return true; 2333 } 2334 2335 void RenderObject::dirtyFormattingContext(bool checkContainer) 2336 { 2337 if (m_markedForRepaint && !checkContainer) { 2338 return; 2339 } 2340 m_markedForRepaint = true; 2341 if (layer() && (style()->position() == PFIXED || style()->position() == PABSOLUTE)) { 2342 return; 2343 } 2344 if (m_parent && (checkContainer || style()->width().isAuto() || style()->height().isAuto() || 2345 !(isFloating() || flowAroundFloats() || isTableCell()))) { 2346 m_parent->dirtyFormattingContext(false); 2347 } 2348 } 2349 2350 void RenderObject::repaintDuringLayout() 2351 { 2352 if (canvas()->needsFullRepaint() || isText()) { 2353 return; 2354 } 2355 if (layer() && !isInlineFlow()) { 2356 layer()->repaint(NormalPriority, true); 2357 } else { 2358 repaint(); 2359 canvas()->deferredRepaint(this); 2360 } 2361 } 2362 2363 void RenderObject::updateBackgroundImages(RenderStyle *oldStyle) 2364 { 2365 // FIXME: This will be slow when a large number of images is used. Fix by using a dict. 2366 const BackgroundLayer *oldLayers = oldStyle ? oldStyle->backgroundLayers() : nullptr; 2367 const BackgroundLayer *newLayers = m_style ? m_style->backgroundLayers() : nullptr; 2368 for (const BackgroundLayer *currOld = oldLayers; currOld; currOld = currOld->next()) { 2369 if (currOld->backgroundImage() && (!newLayers || !newLayers->containsImage(currOld->backgroundImage()))) { 2370 currOld->backgroundImage()->deref(this); 2371 } 2372 } 2373 for (const BackgroundLayer *currNew = newLayers; currNew; currNew = currNew->next()) { 2374 if (currNew->backgroundImage() && (!oldLayers || !oldLayers->containsImage(currNew->backgroundImage()))) { 2375 currNew->backgroundImage()->ref(this); 2376 } 2377 } 2378 } 2379 2380 QRect RenderObject::viewRect() const 2381 { 2382 return containingBlock()->viewRect(); 2383 } 2384 2385 bool RenderObject::absolutePosition(int &xPos, int &yPos, bool f) const 2386 { 2387 RenderObject *p = parent(); 2388 if (p) { 2389 p->absolutePosition(xPos, yPos, f); 2390 if (p->hasOverflowClip()) { 2391 p->layer()->subtractScrollOffset(xPos, yPos); 2392 } 2393 return true; 2394 } else { 2395 xPos = yPos = 0; 2396 return false; 2397 } 2398 } 2399 2400 void RenderObject::caretPos(int /*offset*/, int /*flags*/, int &_x, int &_y, int &width, int &height) const 2401 { 2402 _x = _y = height = -1; 2403 width = 1; // the caret has a default width of one pixel. If you want 2404 // to check for validity, only test the x-coordinate for >= 0. 2405 } 2406 2407 int RenderObject::paddingTop() const 2408 { 2409 int w = 0; 2410 Length padding = m_style->paddingTop(); 2411 if (padding.isPercent()) { 2412 w = containingBlock()->contentWidth(); 2413 } 2414 w = padding.minWidth(w); 2415 if (isTableCell() && padding.isAuto()) { 2416 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding(); 2417 } 2418 return w; 2419 } 2420 2421 int RenderObject::paddingBottom() const 2422 { 2423 int w = 0; 2424 Length padding = style()->paddingBottom(); 2425 if (padding.isPercent()) { 2426 w = containingBlock()->contentWidth(); 2427 } 2428 w = padding.minWidth(w); 2429 if (isTableCell() && padding.isAuto()) { 2430 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding(); 2431 } 2432 return w; 2433 } 2434 2435 int RenderObject::paddingLeft() const 2436 { 2437 int w = 0; 2438 Length padding = style()->paddingLeft(); 2439 if (padding.isPercent()) { 2440 w = containingBlock()->contentWidth(); 2441 } 2442 w = padding.minWidth(w); 2443 if (isTableCell() && padding.isAuto()) { 2444 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding(); 2445 } 2446 return w; 2447 } 2448 2449 int RenderObject::paddingRight() const 2450 { 2451 int w = 0; 2452 Length padding = style()->paddingRight(); 2453 if (padding.isPercent()) { 2454 w = containingBlock()->contentWidth(); 2455 } 2456 w = padding.minWidth(w); 2457 if (isTableCell() && padding.isAuto()) { 2458 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding(); 2459 } 2460 return w; 2461 } 2462 2463 RenderObject *RenderObject::container() const 2464 { 2465 // This method is extremely similar to containingBlock(), but with a few notable 2466 // exceptions. 2467 // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when 2468 // the object is not part of the primary document subtree yet. 2469 // (2) For normal flow elements, it just returns the parent. 2470 // (3) For absolute positioned elements, it will return a relative positioned inline. 2471 // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle 2472 // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and 2473 // calcAbsoluteVertical have to use container(). 2474 EPosition pos = m_style->position(); 2475 RenderObject *o = nullptr; 2476 if (pos == PFIXED) { 2477 // container() can be called on an object that is not in the 2478 // tree yet. We don't call canvas() since it will assert if it 2479 // can't get back to the canvas. Instead we just walk as high up 2480 // as we can. If we're in the tree, we'll get the root. If we 2481 // aren't we'll get the root of our little subtree (most likely 2482 // we'll just return 0). 2483 o = parent(); 2484 while (o && o->parent()) { 2485 o = o->parent(); 2486 } 2487 } else if (pos == PABSOLUTE) { 2488 // Same goes here. We technically just want our containing block, but 2489 // we may not have one if we're part of an uninstalled subtree. We'll 2490 // climb as high as we can though. 2491 o = parent(); 2492 while (o && o->style()->position() == PSTATIC && !o->isCanvas()) { 2493 o = o->parent(); 2494 } 2495 } else { 2496 o = parent(); 2497 } 2498 return o; 2499 } 2500 2501 DOM::DocumentImpl *RenderObject::document() const 2502 { 2503 return m_node->document(); 2504 } 2505 2506 void RenderObject::removeFromObjectLists() 2507 { 2508 // in destruction mode, don't care. 2509 if (documentBeingDestroyed()) { 2510 return; 2511 } 2512 2513 if (isFloating()) { 2514 RenderBlock *outermostBlock = containingBlock(); 2515 for (RenderBlock *p = outermostBlock; p && !p->isCanvas() && p->containsFloat(this);) { 2516 outermostBlock = p; 2517 if (p->isFloatingOrPositioned()) { 2518 break; 2519 } 2520 p = p->containingBlock(); 2521 } 2522 2523 if (outermostBlock) { 2524 outermostBlock->markAllDescendantsWithFloatsForLayout(this); 2525 } 2526 2527 RenderObject *p; 2528 for (p = parent(); p; p = p->parent()) { 2529 if (p->isRenderBlock()) { 2530 static_cast<RenderBlock *>(p)->removeFloatingObject(this); 2531 } 2532 } 2533 2534 } 2535 2536 if (inPosObjectList()) { 2537 RenderObject *p; 2538 for (p = parent(); p; p = p->parent()) { 2539 if (p->isRenderBlock()) { 2540 static_cast<RenderBlock *>(p)->removePositionedObject(this); 2541 } 2542 } 2543 } 2544 } 2545 2546 RenderArena *RenderObject::renderArena() const 2547 { 2548 return m_node->document()->renderArena(); 2549 } 2550 2551 void RenderObject::detach() 2552 { 2553 detachCounters(); 2554 remove(); 2555 2556 // make sure our DOM-node don't think we exist 2557 if (node() && node()->renderer() == this) { 2558 node()->setRenderer(nullptr); 2559 } 2560 2561 // by default no refcounting 2562 arenaDelete(renderArena(), this); 2563 } 2564 2565 void RenderObject::remove() 2566 { 2567 if (m_parent) { 2568 m_parent->removeChild(this); 2569 if (isFloating() || inPosObjectList()) { 2570 removeFromObjectLists(); 2571 } 2572 } 2573 } 2574 2575 void RenderObject::arenaDelete(RenderArena *arena, void *base) 2576 { 2577 #ifndef NDEBUG 2578 void *savedBase = baseOfRenderObjectBeingDeleted; 2579 baseOfRenderObjectBeingDeleted = base; 2580 #endif 2581 delete this; 2582 #ifndef NDEBUG 2583 baseOfRenderObjectBeingDeleted = savedBase; 2584 #endif 2585 2586 // Recover the size left there for us by operator delete and free the memory. 2587 arena->free(*(size_t *)base, base); 2588 } 2589 2590 void RenderObject::arenaDelete(RenderArena *arena) 2591 { 2592 // static_cast unfortunately doesn't work, since we multiple inherit 2593 // in eg. RenderWidget. 2594 arenaDelete(arena, dynamic_cast<void *>(this)); 2595 } 2596 2597 RenderPosition RenderObject::positionForCoordinates(int /*x*/, int /*y*/) 2598 { 2599 return RenderPosition(element(), caretMinOffset()); 2600 } 2601 2602 bool RenderObject::isPointInsideSelection(int x, int y, const Selection &sel) const 2603 { 2604 SelectionState selstate = selectionState(); 2605 if (selstate == SelectionInside) { 2606 return true; 2607 } 2608 if (selstate == SelectionNone || !element()) { 2609 return false; 2610 } 2611 return element()->isPointInsideSelection(x, y, sel); 2612 } 2613 2614 #if 0 2615 FindSelectionResult RenderObject::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &state) 2616 { 2617 #if 0 2618 NodeInfo info(true, false); 2619 if (nodeAtPoint(info, _x, _y, _tx, _ty) && info.innerNode()) { 2620 RenderObject *r = info.innerNode()->renderer(); 2621 if (r) { 2622 if (r == this) { 2623 node = info.innerNode(); 2624 offset = 0; // we have no text... 2625 return SelectionPointInside; 2626 } else { 2627 return r->checkSelectionPoint(_x, _y, _tx, _ty, node, offset, state); 2628 } 2629 } 2630 } 2631 //qCDebug(KHTML_LOG) << "nodeAtPoint Failed. Fallback - hmm, SelectionPointAfter"; 2632 node = 0; 2633 offset = 0; 2634 return SelectionPointAfter; 2635 #endif 2636 int off = offset; 2637 DOM::NodeImpl *nod = node; 2638 2639 for (RenderObject *child = firstChild(); child; child = child->nextSibling()) { 2640 // ignore empty text boxes, they produce totally bogus information 2641 // for caret navigation (LS) 2642 if (child->isText() && !static_cast<RenderText *>(child)->firstTextBox()) { 2643 continue; 2644 } 2645 2646 // qCDebug(KHTML_LOG) << "iterating " << (child ? child->renderName() : "") << "@" << child << (child->isText() ? " contains: \"" + QString::fromRawData(static_cast<RenderText *>(child)->text(), qMin(static_cast<RenderText *>(child)->length(), 10u)) + "\"" : QString()); 2647 // qCDebug(KHTML_LOG) << "---------- checkSelectionPoint recursive -----------"; 2648 khtml::FindSelectionResult pos = child->checkSelectionPoint(_x, _y, _tx + xPos(), _ty + yPos(), nod, off, state); 2649 // qCDebug(KHTML_LOG) << "-------- end checkSelectionPoint recursive ---------"; 2650 // qCDebug(KHTML_LOG) << this << " child->findSelectionNode returned result=" << pos << " nod=" << nod << " off=" << off; 2651 switch (pos) { 2652 case SelectionPointBeforeInLine: 2653 case SelectionPointInside: 2654 //qCDebug(KHTML_LOG) << "RenderObject::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset; 2655 node = nod; 2656 offset = off; 2657 return SelectionPointInside; 2658 case SelectionPointBefore: 2659 //x,y is before this element -> stop here 2660 if (state.m_lastNode) { 2661 node = state.m_lastNode; 2662 offset = state.m_lastOffset; 2663 //qCDebug(KHTML_LOG) << "RenderObject::checkSelectionPoint " << this << " before this child " 2664 // << node << "-> returning SelectionPointInside, offset=" << offset; 2665 return SelectionPointInside; 2666 } else { 2667 node = nod; 2668 offset = off; 2669 //qCDebug(KHTML_LOG) << "RenderObject::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset; 2670 return SelectionPointBefore; 2671 } 2672 break; 2673 case SelectionPointAfter: 2674 if (state.m_afterInLine) { 2675 break; 2676 } 2677 // fall through 2678 case SelectionPointAfterInLine: 2679 if (pos == SelectionPointAfterInLine) { 2680 state.m_afterInLine = true; 2681 } 2682 //qCDebug(KHTML_LOG) << "RenderObject::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine; 2683 state.m_lastNode = nod; 2684 state.m_lastOffset = off; 2685 // No "return" here, obviously. We must keep looking into the children. 2686 break; 2687 } 2688 } 2689 // If we are after the last child, return lastNode/lastOffset 2690 // But lastNode can be 0L if there is no child, for instance. 2691 if (state.m_lastNode) { 2692 node = state.m_lastNode; 2693 offset = state.m_lastOffset; 2694 } 2695 //qCDebug(KHTML_LOG) << "fallback - SelectionPointAfter node=" << node << " offset=" << offset; 2696 return SelectionPointAfter; 2697 } 2698 #endif 2699 2700 bool RenderObject::mouseInside() const 2701 { 2702 if (!m_mouseInside && continuation()) { 2703 return continuation()->mouseInside(); 2704 } 2705 return m_mouseInside; 2706 } 2707 2708 bool RenderObject::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside) 2709 { 2710 int tx = _tx + xPos(); 2711 int ty = _ty + yPos(); 2712 2713 inside |= (style()->visibility() != HIDDEN && 2714 (_y >= ty) && (_y < ty + height()) && (_x >= tx) && (_x < tx + width())) || isRoot() || isBody(); 2715 bool inOverflowRect = inside; 2716 if (!inOverflowRect) { 2717 int ol = overflowLeft(); 2718 int ot = overflowTop(); 2719 QRect overflowRect(tx + ol, ty + ot, overflowWidth() - ol, overflowHeight() - ot); 2720 inOverflowRect = overflowRect.contains(_x, _y); 2721 } 2722 2723 // ### table should have its own, more performant method 2724 if (hitTestAction != HitTestSelfOnly && 2725 ((!isRenderBlock() || 2726 !static_cast<RenderBlock *>(this)->isPointInScrollbar(_x, _y, _tx, _ty)) && 2727 (inOverflowRect || isInline() || isRoot() || isCanvas() || 2728 isTableRow() || isTableSection() || inside || mouseInside()))) { 2729 if (hitTestAction == HitTestChildrenOnly) { 2730 inside = false; 2731 } 2732 if (hasOverflowClip() && layer()) { 2733 layer()->subtractScrollOffset(tx, ty); 2734 } 2735 for (RenderObject *child = lastChild(); child; child = child->previousSibling()) 2736 if (!child->layer() && child->nodeAtPoint(info, _x, _y, tx, ty, HitTestAll)) { 2737 inside = true; 2738 } 2739 } 2740 2741 if (inside) { 2742 setInnerNode(info); 2743 } 2744 2745 return inside; 2746 } 2747 2748 void RenderObject::setInnerNode(NodeInfo &info) 2749 { 2750 if (!info.innerNode() && !isInline() && continuation()) { 2751 // We are in the margins of block elements that are part of a continuation. In 2752 // this case we're actually still inside the enclosing inline element that was 2753 // split. Go ahead and set our inner node accordingly. 2754 info.setInnerNode(continuation()->element()); 2755 if (!info.innerNonSharedNode()) { 2756 info.setInnerNonSharedNode(continuation()->element()); 2757 } 2758 } 2759 2760 if (!info.innerNode() && element()) { 2761 info.setInnerNode(element()); 2762 } 2763 2764 if (!info.innerNonSharedNode() && element()) { 2765 info.setInnerNonSharedNode(element()); 2766 } 2767 } 2768 2769 short RenderObject::verticalPositionHint(bool firstLine) const 2770 { 2771 short vpos = m_verticalPosition; 2772 if (m_verticalPosition == PositionUndefined || firstLine) { 2773 vpos = getVerticalPosition(firstLine); 2774 if (!firstLine) { 2775 const_cast<RenderObject *>(this)->m_verticalPosition = vpos; 2776 } 2777 } 2778 return vpos; 2779 2780 } 2781 2782 short RenderObject::getVerticalPosition(bool firstLine, RenderObject *ref) const 2783 { 2784 // vertical align for table cells has a different meaning 2785 int vpos = 0; 2786 if (!isTableCell() && isInline()) { 2787 EVerticalAlign va = style()->verticalAlign(); 2788 if (va == TOP) { 2789 vpos = PositionTop; 2790 } else if (va == BOTTOM) { 2791 vpos = PositionBottom; 2792 } else { 2793 if (!ref) { 2794 ref = parent(); 2795 } 2796 bool checkParent = ref->isInline() && !ref->isReplacedBlock() && 2797 !(ref->style()->verticalAlign() == TOP || ref->style()->verticalAlign() == BOTTOM); 2798 vpos = checkParent ? ref->verticalPositionHint(firstLine) : 0; 2799 // don't allow elements nested inside text-top to have a different valignment. 2800 if (va == BASELINE) { 2801 return vpos; 2802 } else if (va == LENGTH) { 2803 return vpos - style()->verticalAlignLength().width(lineHeight(firstLine)); 2804 } 2805 2806 const QFont &f = ref->font(firstLine); 2807 int fontsize = f.pixelSize(); 2808 2809 if (va == SUB) { 2810 vpos += fontsize / 5 + 1; 2811 } else if (va == SUPER) { 2812 vpos -= fontsize / 3 + 1; 2813 } else if (va == TEXT_TOP) { 2814 vpos += baselinePosition(firstLine) - (QFontMetrics(f).ascent() + QFontMetrics(f).leading() / 2); 2815 } else if (va == MIDDLE) { 2816 QRect b = QFontMetrics(f).boundingRect('x'); 2817 vpos += -b.height() / 2 - lineHeight(firstLine) / 2 + baselinePosition(firstLine); 2818 } else if (va == TEXT_BOTTOM) { 2819 vpos += QFontMetrics(f).descent() + QFontMetrics(f).leading() / 2; 2820 if (!isReplaced()) { 2821 vpos -= (lineHeight(firstLine) - baselinePosition(firstLine)); 2822 } 2823 } else if (va == BASELINE_MIDDLE) { 2824 vpos += - lineHeight(firstLine) / 2 + baselinePosition(firstLine); 2825 } 2826 } 2827 } 2828 return vpos; 2829 } 2830 2831 short RenderObject::lineHeight(bool firstLine) const 2832 { 2833 // Inline blocks are replaced elements. Otherwise, just pass off to 2834 // the base class. If we're being queried as though we're the root line 2835 // box, then the fact that we're an inline-block is irrelevant, and we behave 2836 // just like a block. 2837 2838 if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout())) { 2839 return height() + marginTop() + marginBottom(); 2840 } 2841 2842 Length lh; 2843 if (firstLine && hasFirstLine()) { 2844 RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE); 2845 if (pseudoStyle) { 2846 lh = pseudoStyle->lineHeight(); 2847 } 2848 } else { 2849 lh = style()->lineHeight(); 2850 } 2851 2852 // its "unset", choose nice default 2853 if (lh.isNegative()) { 2854 return style()->htmlFont().lineSpacing(); 2855 } 2856 2857 if (lh.isPercent()) { 2858 return lh.minWidth(style()->font().pixelSize()); 2859 } 2860 2861 // its fixed 2862 return lh.value(); 2863 } 2864 2865 short RenderObject::baselinePosition(bool firstLine) const 2866 { 2867 // If we're an inline-block and need layout, it means our replaced boundaries 2868 // are not yet fully established, so we behave just like a block. 2869 if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout())) { 2870 return height() + marginTop() + marginBottom(); 2871 } 2872 2873 const QFontMetrics &fm = fontMetrics(firstLine); 2874 return fm.ascent() + (lineHeight(firstLine) - fm.height()) / 2; 2875 } 2876 2877 void RenderObject::invalidateVerticalPosition() 2878 { 2879 m_verticalPosition = PositionUndefined; 2880 } 2881 2882 void RenderObject::recalcMinMaxWidths() 2883 { 2884 KHTMLAssert(m_recalcMinMax); 2885 2886 #ifdef DEBUG_LAYOUT 2887 qCDebug(KHTML_LOG) << renderName() << " recalcMinMaxWidths() this=" << this; 2888 #endif 2889 2890 RenderObject *child = firstChild(); 2891 int cmin = 0; 2892 int cmax = 0; 2893 2894 while (child) { 2895 bool test = false; 2896 if ((m_minMaxKnown && child->m_recalcMinMax) || !child->m_minMaxKnown) { 2897 cmin = child->minWidth(); 2898 cmax = child->maxWidth(); 2899 test = true; 2900 } 2901 if (child->m_recalcMinMax) { 2902 child->recalcMinMaxWidths(); 2903 } 2904 if (!child->m_minMaxKnown) { 2905 child->calcMinMaxWidth(); 2906 } 2907 if (m_minMaxKnown && test && (cmin != child->minWidth() || cmax != child->maxWidth())) { 2908 m_minMaxKnown = false; 2909 } 2910 child = child->nextSibling(); 2911 } 2912 2913 // we need to recalculate, if the contains inline children, as the change could have 2914 // happened somewhere deep inside the child tree 2915 if ((!isInline() || isReplacedBlock()) && childrenInline()) { 2916 m_minMaxKnown = false; 2917 } 2918 2919 if (!m_minMaxKnown) { 2920 calcMinMaxWidth(); 2921 } 2922 m_recalcMinMax = false; 2923 } 2924 2925 void RenderObject::scheduleRelayout(RenderObject *clippedObj) 2926 { 2927 if (!isCanvas()) { 2928 return; 2929 } 2930 KHTMLView *view = static_cast<RenderCanvas *>(this)->view(); 2931 if (view) { 2932 view->scheduleRelayout(clippedObj); 2933 } 2934 } 2935 2936 InlineBox *RenderObject::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/) 2937 { 2938 KHTMLAssert(false); 2939 return nullptr; 2940 } 2941 2942 void RenderObject::getTextDecorationColors(int decorations, QColor &underline, QColor &overline, 2943 QColor &linethrough, bool quirksMode) 2944 { 2945 RenderObject *curr = this; 2946 do { 2947 RenderStyle *st = curr->style(); 2948 int currDecs = st->textDecoration(); 2949 if (currDecs) { 2950 if (currDecs & UNDERLINE) { 2951 decorations &= ~UNDERLINE; 2952 underline = st->color(); 2953 } 2954 if (currDecs & OVERLINE) { 2955 decorations &= ~OVERLINE; 2956 overline = st->color(); 2957 } 2958 if (currDecs & LINE_THROUGH) { 2959 decorations &= ~LINE_THROUGH; 2960 linethrough = st->color(); 2961 } 2962 } 2963 curr = curr->parent(); 2964 if (curr && curr->isRenderBlock() && curr->continuation()) { 2965 curr = curr->continuation(); 2966 } 2967 } while (curr && decorations && (!quirksMode || !curr->element() || 2968 (curr->element()->id() != ID_A && curr->element()->id() != ID_FONT))); 2969 2970 // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). 2971 if (decorations && curr) { 2972 RenderStyle *st = curr->style(); 2973 if (decorations & UNDERLINE) { 2974 underline = st->color(); 2975 } 2976 if (decorations & OVERLINE) { 2977 overline = st->color(); 2978 } 2979 if (decorations & LINE_THROUGH) { 2980 linethrough = st->color(); 2981 } 2982 } 2983 } 2984 2985 int RenderObject::maximalOutlineSize(PaintAction p) const 2986 { 2987 if (p != PaintActionOutline) { 2988 return 0; 2989 } 2990 return static_cast<RenderCanvas *>(document()->renderer())->maximalOutlineSize(); 2991 } 2992 2993 void RenderObject::collectBorders(QList<CollapsedBorderValue> &borderStyles) 2994 { 2995 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 2996 curr->collectBorders(borderStyles); 2997 } 2998 } 2999 3000 bool RenderObject::flowAroundFloats() const 3001 { 3002 return isReplaced() || hasOverflowClip() || style()->flowAroundFloats(); 3003 } 3004 3005 bool RenderObject::usesLineWidth() const 3006 { 3007 // All auto-width objects that avoid floats should always use lineWidth 3008 // unless they are floating or inline. We only care about objects that grow 3009 // to fill the available space. 3010 return (!isInline() || isHTMLMarquee()) && flowAroundFloats() && style()->width().isAuto() && !isFloating(); 3011 } 3012 3013 long RenderObject::caretMinOffset() const 3014 { 3015 return 0; 3016 } 3017 3018 long RenderObject::caretMaxOffset() const 3019 { 3020 return 0; 3021 } 3022 3023 unsigned long RenderObject::caretMaxRenderedOffset() const 3024 { 3025 return 0; 3026 } 3027 3028 InlineBox *RenderObject::inlineBox(long /*offset*/) 3029 { 3030 if (isBox()) { 3031 return static_cast<RenderBox *>(this)->placeHolderBox(); 3032 } 3033 return nullptr; 3034 } 3035 3036 bool RenderObject::hasCounter(const DOMString &counter) const 3037 { 3038 if (style() && (!isText() || isCounter())) { 3039 if (lookupCounter(counter)) { 3040 return true; 3041 } 3042 if (style()->hasCounterReset(counter)) { 3043 return true; 3044 } else if (style()->hasCounterIncrement(counter)) { 3045 return true; 3046 } 3047 } 3048 if (counter == "list-item") { 3049 if (isListItem()) { 3050 return true; 3051 } 3052 if (element() && ( 3053 element()->id() == ID_OL || 3054 element()->id() == ID_UL || 3055 element()->id() == ID_MENU || 3056 element()->id() == ID_DIR)) { 3057 return true; 3058 } 3059 } else if (counter == "-khtml-quotes" && isQuote()) { 3060 return (static_cast<const RenderQuote *>(this)->quoteCount() != 0); 3061 } 3062 return false; 3063 } 3064 3065 CounterNode *RenderObject::getCounter(const DOMString &counter, bool view, bool counters) 3066 { 3067 // qCDebug(KHTML_LOG) << renderName() << " getCounter(" << counter << ")"; 3068 3069 if (!style()) { 3070 return nullptr; 3071 } 3072 3073 if (isText() && !isCounter()) { 3074 return nullptr; 3075 } 3076 3077 CounterNode *i = lookupCounter(counter); 3078 if (i) { 3079 return i; 3080 } 3081 int val = 0; 3082 3083 if (style()->hasCounterReset(counter) || isRoot()) { 3084 i = new CounterReset(this); 3085 val = style()->counterReset(counter); 3086 if (style()->hasCounterIncrement(counter)) { 3087 val += style()->counterIncrement(counter); 3088 } 3089 // qCDebug(KHTML_LOG) << renderName() << " counter-reset: " << counter << " " << val; 3090 } else if (style()->hasCounterIncrement(counter)) { 3091 i = new CounterNode(this); 3092 val = style()->counterIncrement(counter); 3093 // qCDebug(KHTML_LOG) << renderName() << " counter-increment: " << counter << " " << val; 3094 } else if (counter == "list-item") { 3095 if (isListItem()) { 3096 if (element() && element()->id() == ID_LI) { 3097 DOMString v = static_cast<ElementImpl *>(element())->getAttribute(ATTR_VALUE); 3098 if (!v.isEmpty()) { 3099 i = new CounterReset(this); 3100 val = v.toInt(); 3101 // qCDebug(KHTML_LOG) << renderName() << " counter-reset: " << counter << " " << val; 3102 } 3103 } 3104 if (!i) { 3105 i = new CounterNode(this); 3106 val = 1; 3107 // qCDebug(KHTML_LOG) << renderName() << " counter-increment: " << counter << " " << val; 3108 } 3109 } else if (element() && element()->id() == ID_OL) { 3110 i = new CounterReset(this); 3111 DOMString v = static_cast<ElementImpl *>(element())->getAttribute(ATTR_START); 3112 if (!v.isEmpty()) { 3113 val = v.toInt() - 1; 3114 } else { 3115 val = 0; 3116 } 3117 // qCDebug(KHTML_LOG) << renderName() << " counter-reset: " << counter << " " << val; 3118 } else if (element() && 3119 (element()->id() == ID_UL || 3120 element()->id() == ID_MENU || 3121 element()->id() == ID_DIR)) { 3122 i = new CounterReset(this); 3123 val = 0; 3124 // qCDebug(KHTML_LOG) << renderName() << " counter-reset: " << counter << " " << val; 3125 } 3126 } else if (counter == "-khtml-quotes" && isQuote()) { 3127 i = new CounterNode(this); 3128 val = static_cast<RenderQuote *>(this)->quoteCount(); 3129 } 3130 3131 if (!i) { 3132 i = new CounterNode(this); 3133 val = 0; 3134 // qCDebug(KHTML_LOG) << renderName() << " counter-increment: " << counter << " " << val; 3135 } 3136 i->setValue(val); 3137 if (view) { 3138 i->setIsVisual(); 3139 } 3140 if (counters) { 3141 i->setHasCounters(); 3142 } 3143 3144 insertCounter(counter, i); 3145 3146 if (!isRoot()) { 3147 CounterNode *last = nullptr, *current = nullptr; 3148 RenderObject *n = previousSibling(); 3149 while (n) { 3150 if (n->hasCounter(counter)) { 3151 current = n->getCounter(counter); 3152 break; 3153 } else { 3154 n = n->previousSibling(); 3155 } 3156 } 3157 last = current; 3158 3159 CounterNode *sibling = current; 3160 // counter-reset on same render-level is our counter-parent 3161 if (last) { 3162 // Found render-sibling, now search for later counter-siblings among its render-children 3163 n = n->lastChild(); 3164 while (n) { 3165 if (n->hasCounter(counter)) { 3166 current = n->getCounter(counter); 3167 if (last->parent() == current->parent() || sibling == current->parent()) { 3168 last = current; 3169 // If the current counter is not the last, search deeper 3170 if (current->nextSibling()) { 3171 n = n->lastChild(); 3172 continue; 3173 } else { 3174 break; 3175 } 3176 } 3177 } 3178 n = n->previousSibling(); 3179 } 3180 if (sibling->isReset()) { 3181 if (last != sibling) { 3182 sibling->insertAfter(i, last); 3183 } else { 3184 sibling->insertAfter(i, nullptr); 3185 } 3186 } else if (last->parent()) { 3187 last->parent()->insertAfter(i, last); 3188 } 3189 } else if (parent()) { 3190 // Nothing found among siblings, let our parent search 3191 last = parent()->getCounter(counter, false); 3192 if (last->isReset()) { 3193 last->insertAfter(i, nullptr); 3194 } else if (last->parent()) { 3195 last->parent()->insertAfter(i, last); 3196 } 3197 } 3198 } 3199 3200 return i; 3201 } 3202 3203 CounterNode *RenderObject::lookupCounter(const DOMString &counter) const 3204 { 3205 QHash<DOMString, khtml::CounterNode *> *counters = document()->counters(this); 3206 return counters ? counters->value(counter) : nullptr; 3207 } 3208 3209 void RenderObject::detachCounters() 3210 { 3211 QHash<DOMString, khtml::CounterNode *> *counters = document()->counters(this); 3212 if (!counters) { 3213 return; 3214 } 3215 3216 QHashIterator<DOMString, khtml::CounterNode *> i(*counters); 3217 3218 while (i.hasNext()) { 3219 i.next(); 3220 i.value()->remove(); 3221 delete i.value(); 3222 } 3223 document()->removeCounters(this); 3224 } 3225 3226 void RenderObject::insertCounter(const DOMString &counter, CounterNode *val) 3227 { 3228 QHash<DOMString, khtml::CounterNode *> *counters = document()->counters(this); 3229 3230 if (!counters) { 3231 counters = new QHash<DOMString, khtml::CounterNode *>(); 3232 document()->setCounters(this, counters); 3233 } 3234 3235 counters->insert(counter, val); 3236 } 3237 3238 void RenderObject::updateWidgetMasks() 3239 { 3240 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) { 3241 if (curr->isWidget() && static_cast<RenderWidget *>(curr)->needsMask()) { 3242 QWidget *w = static_cast<RenderWidget *>(curr)->widget(); 3243 if (!w) { 3244 return; 3245 } 3246 RenderLayer *l = curr->enclosingStackingContext(); 3247 QRegion r = l ? l->getMask() : QRegion(); 3248 int x, y; 3249 if (!r.isEmpty() && curr->absolutePosition(x, y)) { 3250 int pbx = curr->borderLeft() + curr->paddingLeft(); 3251 int pby = curr->borderTop() + curr->paddingTop(); 3252 x += pbx; 3253 y += pby; 3254 r = r.intersect(QRect(x, y, 3255 curr->width() - pbx - curr->borderRight() - curr->paddingRight(), 3256 curr->height() - pby - curr->borderBottom() - curr->paddingBottom())); 3257 #ifdef MASK_DEBUG 3258 QVector<QRect> ar = r.rects(); 3259 qCDebug(KHTML_LOG) << "|| Setting widget mask for " << curr->information(); 3260 for (int i = 0; i < ar.size(); ++i) { 3261 qCDebug(KHTML_LOG) << " " << ar[i]; 3262 } 3263 #endif 3264 r.translate(-x, -y); 3265 3266 // ### Scrollarea's widget doesn't update when mask change. 3267 // Might be a Qt bug. Might be the way we handle updates. Investigate. 3268 if (::qobject_cast<QScrollArea *>(w)) { 3269 QScrollArea *sa = static_cast<QScrollArea *>(w); 3270 if (!w->mask().isEmpty()) { 3271 QPoint off(sa->horizontalScrollBar()->value(), 3272 sa->verticalScrollBar()->value()); 3273 sa->widget()->update(w->mask().translated(off)); 3274 sa->horizontalScrollBar()->update(); 3275 sa->verticalScrollBar()->update(); 3276 } 3277 } 3278 w->setMask(r); 3279 } else { 3280 w->clearMask(); 3281 } 3282 } else if (!curr->layer() || !curr->layer()->isStackingContext()) { 3283 curr->updateWidgetMasks(); 3284 } 3285 3286 } 3287 } 3288 3289 QRegion RenderObject::visibleFlowRegion(int x, int y) const 3290 { 3291 QRegion r; 3292 bool returnSelf = false; 3293 for (RenderObject *ro = firstChild(); ro; ro = ro->nextSibling()) { 3294 if (!ro->layer() && !ro->isFloating() && ro->style()->visibility() == VISIBLE) { 3295 const RenderStyle *s = ro->style(); 3296 int ow = s->outlineSize(); 3297 if (ro->isInlineFlow() || ro->isText()) { 3298 returnSelf = true; 3299 break; 3300 } 3301 if (s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() || ro->isReplaced() || ow) { 3302 r += QRect(x - ow + ro->effectiveXPos(), y - ow + ro->effectiveYPos(), 3303 ro->effectiveWidth() + ow * 2, ro->effectiveHeight() + ow * 2); 3304 } else { 3305 r += ro->visibleFlowRegion(x + ro->xPos(), y + ro->yPos()); 3306 } 3307 } 3308 } 3309 if (hasFloats()) { 3310 r += static_cast<const RenderBlock *>(this)->visibleFloatingRegion(x, y); 3311 } 3312 if (returnSelf) { 3313 int ow = style()->outlineSize(); 3314 r += QRect(x - xPos() - ow + effectiveXPos(), y - yPos() - ow + effectiveYPos(), 3315 effectiveWidth() + ow * 2, effectiveHeight() + ow * 2); 3316 } 3317 return r; 3318 } 3319 3320 // SVG 3321 FloatRect RenderObject::relativeBBox(bool includeStroke) const 3322 { 3323 Q_UNUSED(includeStroke); 3324 return FloatRect(); 3325 } 3326 3327 AffineTransform RenderObject::localTransform() const 3328 { 3329 return AffineTransform(1, 0, 0, 1, xPos(), yPos()); 3330 } 3331 3332 AffineTransform RenderObject::absoluteTransform() const 3333 { 3334 if (parent()) { 3335 return localTransform() * parent()->absoluteTransform(); 3336 } 3337 return localTransform(); 3338 } 3339 // END SVG 3340 3341 #undef RED_LUMINOSITY 3342 #undef GREEN_LUMINOSITY 3343 #undef BLUE_LUMINOSITY 3344 #undef INTENSITY_FACTOR 3345 #undef LIGHT_FACTOR 3346 #undef LUMINOSITY_FACTOR 3347 3348 #undef MAX_COLOR 3349 #undef COLOR_DARK_THRESHOLD 3350 #undef COLOR_LIGHT_THRESHOLD 3351 3352 #undef COLOR_LITE_BS_FACTOR 3353 #undef COLOR_LITE_TS_FACTOR 3354 3355 #undef COLOR_DARK_BS_FACTOR 3356 #undef COLOR_DARK_TS_FACTOR 3357 3358 #undef LIGHT_GRAY 3359 #undef DARK_GRAY 3360