1 /**
2 * This file is part of the html renderer for KDE.
3 *
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
7 * (C) 2004-2008 Apple Computer, Inc.
8 * (C) 2006 Germain Garand <germain@ebooksfrance.org>
9 * (C) 2008-2009 Fredrik Höglund <fredrik@kde.org>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
25 *
26 */
27
28 #include "rendering/render_object.h"
29 #include "rendering/render_table.h"
30 #include "rendering/render_list.h"
31 #include "rendering/render_canvas.h"
32 #include "rendering/render_block.h"
33 #include "rendering/render_arena.h"
34 #include "rendering/render_layer.h"
35 #include "rendering/render_line.h"
36 #include "rendering/render_inline.h"
37 #include "rendering/render_text.h"
38 #include "rendering/render_replaced.h"
39 #include "rendering/render_generated.h"
40 #include "rendering/counter_tree.h"
41
42 #include "xml/dom_elementimpl.h"
43 #include "dom/dom_doc.h"
44 #include "misc/loader.h"
45 #include "misc/borderarcstroker.h"
46
47 #include "khtml_debug.h"
48 #include <QPainter>
49 #include "khtmlview.h"
50 #include <khtml_part.h>
51 #include <QPaintEngine>
52
53 using namespace DOM;
54 using namespace khtml;
55
56 #define RED_LUMINOSITY 30
57 #define GREEN_LUMINOSITY 59
58 #define BLUE_LUMINOSITY 11
59 #define INTENSITY_FACTOR 25
60 #define LIGHT_FACTOR 0
61 #define LUMINOSITY_FACTOR 75
62
63 #define MAX_COLOR 255
64 #define COLOR_DARK_THRESHOLD 51
65 #define COLOR_LIGHT_THRESHOLD 204
66
67 #define COLOR_LITE_BS_FACTOR 45
68 #define COLOR_LITE_TS_FACTOR 70
69
70 #define COLOR_DARK_BS_FACTOR 30
71 #define COLOR_DARK_TS_FACTOR 50
72
73 #define LIGHT_GRAY qRgb(192, 192, 192)
74 #define DARK_GRAY qRgb(96, 96, 96)
75
76 #ifndef NDEBUG
77 static void *baseOfRenderObjectBeingDeleted;
78 #endif
79
80 QCache<quint64, QPixmap> *RenderObject::s_dashedLineCache = nullptr;
81
cleanup()82 void RenderObject::cleanup()
83 {
84 delete s_dashedLineCache;
85 s_dashedLineCache = nullptr;
86 }
87
88 //#define MASK_DEBUG
89
operator new(size_t sz,RenderArena * renderArena)90 void *RenderObject::operator new(size_t sz, RenderArena *renderArena) throw()
91 {
92 return renderArena->allocate(sz);
93 }
94
operator delete(void * ptr,size_t sz)95 void RenderObject::operator delete(void *ptr, size_t sz)
96 {
97 assert(baseOfRenderObjectBeingDeleted == ptr);
98
99 #ifdef KHTML_USE_ARENA_ALLOCATOR
100 // Stash size where detach can find it.
101 *(size_t *)ptr = sz;
102 #endif
103 }
104
createObject(DOM::NodeImpl * node,RenderStyle * style)105 RenderObject *RenderObject::createObject(DOM::NodeImpl *node, RenderStyle *style)
106 {
107 RenderObject *o = nullptr;
108 khtml::RenderArena *arena = node->document()->renderArena();
109 switch (style->display()) {
110 case NONE:
111 break;
112 case INLINE:
113 o = new(arena) RenderInline(node);
114 break;
115 case BLOCK:
116 o = new(arena) RenderBlock(node);
117 break;
118 case INLINE_BLOCK:
119 o = new(arena) RenderBlock(node);
120 break;
121 case LIST_ITEM:
122 o = new(arena) RenderListItem(node);
123 break;
124 case RUN_IN:
125 case COMPACT:
126 o = new(arena) RenderBlock(node);
127 break;
128 case TABLE:
129 case INLINE_TABLE:
130 style->setFlowAroundFloats(true);
131 o = new(arena) RenderTable(node);
132 break;
133 case TABLE_ROW_GROUP:
134 case TABLE_HEADER_GROUP:
135 case TABLE_FOOTER_GROUP:
136 o = new(arena) RenderTableSection(node);
137 break;
138 case TABLE_ROW:
139 o = new(arena) RenderTableRow(node);
140 break;
141 case TABLE_COLUMN_GROUP:
142 case TABLE_COLUMN:
143 o = new(arena) RenderTableCol(node);
144 break;
145 case TABLE_CELL:
146 o = new(arena) RenderTableCell(node);
147 break;
148 case TABLE_CAPTION:
149 o = new(arena) RenderBlock(node);
150 break;
151 }
152 return o;
153 }
154
RenderObject(DOM::NodeImpl * node)155 RenderObject::RenderObject(DOM::NodeImpl *node)
156 : CachedObjectClient(),
157 m_style(nullptr),
158 m_node(node),
159 m_parent(nullptr),
160 m_previous(nullptr),
161 m_next(nullptr),
162 m_verticalPosition(PositionUndefined),
163 m_needsLayout(false),
164 m_normalChildNeedsLayout(false),
165 m_markedForRepaint(false),
166 m_posChildNeedsLayout(false),
167 m_minMaxKnown(false),
168 m_floating(false),
169
170 m_positioned(false),
171 m_relPositioned(false),
172 m_paintBackground(false),
173
174 m_isAnonymous(node->isDocumentNode()),
175 m_recalcMinMax(false),
176 m_isText(false),
177 m_inline(true),
178 m_attached(false),
179
180 m_replaced(false),
181 m_mouseInside(false),
182 m_hasFirstLine(false),
183 m_isSelectionBorder(false),
184 m_isRoot(false),
185 m_afterPageBreak(false),
186 m_needsPageClear(false),
187 m_containsPageBreak(false),
188 m_hasOverflowClip(false),
189 m_inPosObjectList(false),
190 m_doNotDelete(false)
191 {
192 assert(node);
193 if (node->document()->documentElement() == node) {
194 setIsRoot(true);
195 }
196 }
197
~RenderObject()198 RenderObject::~RenderObject()
199 {
200 const BackgroundLayer *bgLayer = m_style->backgroundLayers();
201 while (bgLayer) {
202 if (bgLayer->backgroundImage()) {
203 bgLayer->backgroundImage()->deref(this);
204 }
205 bgLayer = bgLayer->next();
206 }
207
208 m_style->deref();
209 }
210
objectBelow() const211 RenderObject *RenderObject::objectBelow() const
212 {
213 RenderObject *obj = firstChild();
214 if (!obj) {
215 obj = nextSibling();
216 if (!obj) {
217 obj = parent();
218 while (obj && !obj->nextSibling()) {
219 obj = obj->parent();
220 }
221 if (obj) {
222 obj = obj->nextSibling();
223 }
224 }
225 }
226 return obj;
227 }
228
objectAbove() const229 RenderObject *RenderObject::objectAbove() const
230 {
231 RenderObject *obj = previousSibling();
232 if (!obj) {
233 return parent();
234 }
235
236 RenderObject *last = obj->lastChild();
237 while (last) {
238 obj = last;
239 last = last->lastChild();
240 }
241 return obj;
242 }
243 /*
244 bool RenderObject::isRoot() const
245 {
246 return !isAnonymous() &&
247 element()->document()->documentElement() == element();
248 }*/
249
isHR() const250 bool RenderObject::isHR() const
251 {
252 return element() && element()->id() == ID_HR;
253 }
isWordBreak() const254 bool RenderObject::isWordBreak() const
255 {
256 return element() && element()->id() == ID_WBR;
257 }
isHTMLMarquee() const258 bool RenderObject::isHTMLMarquee() const
259 {
260 return element() && element()->renderer() == this && element()->id() == ID_MARQUEE;
261 }
262
addChild(RenderObject *,RenderObject *)263 void RenderObject::addChild(RenderObject *, RenderObject *)
264 {
265 KHTMLAssert(0);
266 }
267
removeChildNode(RenderObject *)268 RenderObject *RenderObject::removeChildNode(RenderObject *)
269 {
270 KHTMLAssert(0);
271 return nullptr;
272 }
273
removeChild(RenderObject *)274 void RenderObject::removeChild(RenderObject *)
275 {
276 KHTMLAssert(0);
277 }
278
appendChildNode(RenderObject *)279 void RenderObject::appendChildNode(RenderObject *)
280 {
281 KHTMLAssert(0);
282 }
283
insertChildNode(RenderObject *,RenderObject *)284 void RenderObject::insertChildNode(RenderObject *, RenderObject *)
285 {
286 KHTMLAssert(0);
287 }
288
nextRenderer() const289 RenderObject *RenderObject::nextRenderer() const
290 {
291 if (firstChild()) {
292 return firstChild();
293 } else if (nextSibling()) {
294 return nextSibling();
295 } else {
296 const RenderObject *r = this;
297 while (r && !r->nextSibling()) {
298 r = r->parent();
299 }
300 if (r) {
301 return r->nextSibling();
302 }
303 }
304 return nullptr;
305 }
306
previousRenderer() const307 RenderObject *RenderObject::previousRenderer() const
308 {
309 if (previousSibling()) {
310 RenderObject *r = previousSibling();
311 while (r->lastChild()) {
312 r = r->lastChild();
313 }
314 return r;
315 } else if (parent()) {
316 return parent();
317 } else {
318 return nullptr;
319 }
320 }
321
isEditable() const322 bool RenderObject::isEditable() const
323 {
324 RenderText *textRenderer = nullptr;
325 if (isText()) {
326 textRenderer = static_cast<RenderText *>(const_cast<RenderObject *>(this));
327 }
328
329 return style()->visibility() == VISIBLE &&
330 element() && element()->isContentEditable() &&
331 ((isBlockFlow() && !firstChild()) ||
332 isReplaced() ||
333 isBR() ||
334 (textRenderer && textRenderer->firstTextBox()));
335 }
336
nextEditable() const337 RenderObject *RenderObject::nextEditable() const
338 {
339 RenderObject *r = const_cast<RenderObject *>(this);
340 RenderObject *n = firstChild();
341 if (n) {
342 while (n) {
343 r = n;
344 n = n->firstChild();
345 }
346 if (r->isEditable()) {
347 return r;
348 } else {
349 return r->nextEditable();
350 }
351 }
352 n = r->nextSibling();
353 if (n) {
354 r = n;
355 while (n) {
356 r = n;
357 n = n->firstChild();
358 }
359 if (r->isEditable()) {
360 return r;
361 } else {
362 return r->nextEditable();
363 }
364 }
365 n = r->parent();
366 while (n) {
367 r = n;
368 n = r->nextSibling();
369 if (n) {
370 r = n;
371 n = r->firstChild();
372 while (n) {
373 r = n;
374 n = n->firstChild();
375 }
376 if (r->isEditable()) {
377 return r;
378 } else {
379 return r->nextEditable();
380 }
381 }
382 n = r->parent();
383 }
384 return nullptr;
385 }
386
previousEditable() const387 RenderObject *RenderObject::previousEditable() const
388 {
389 RenderObject *r = const_cast<RenderObject *>(this);
390 RenderObject *n = firstChild();
391 if (n) {
392 while (n) {
393 r = n;
394 n = n->lastChild();
395 }
396 if (r->isEditable()) {
397 return r;
398 } else {
399 return r->previousEditable();
400 }
401 }
402 n = r->previousSibling();
403 if (n) {
404 r = n;
405 while (n) {
406 r = n;
407 n = n->lastChild();
408 }
409 if (r->isEditable()) {
410 return r;
411 } else {
412 return r->previousEditable();
413 }
414 }
415 n = r->parent();
416 while (n) {
417 r = n;
418 n = r->previousSibling();
419 if (n) {
420 r = n;
421 n = r->lastChild();
422 while (n) {
423 r = n;
424 n = n->lastChild();
425 }
426 if (r->isEditable()) {
427 return r;
428 } else {
429 return r->previousEditable();
430 }
431 }
432 n = r->parent();
433 }
434 return nullptr;
435 }
436
firstLeafChild() const437 RenderObject *RenderObject::firstLeafChild() const
438 {
439 RenderObject *r = firstChild();
440 while (r) {
441 RenderObject *n = nullptr;
442 n = r->firstChild();
443 if (!n) {
444 break;
445 }
446 r = n;
447 }
448 return r;
449 }
450
lastLeafChild() const451 RenderObject *RenderObject::lastLeafChild() const
452 {
453 RenderObject *r = lastChild();
454 while (r) {
455 RenderObject *n = nullptr;
456 n = r->lastChild();
457 if (!n) {
458 break;
459 }
460 r = n;
461 }
462 return r;
463 }
464
addLayers(RenderObject * obj,RenderLayer * parentLayer,RenderObject * & newObject,RenderLayer * & beforeChild)465 static void addLayers(RenderObject *obj, RenderLayer *parentLayer, RenderObject *&newObject,
466 RenderLayer *&beforeChild)
467 {
468 if (obj->layer()) {
469 if (!beforeChild && newObject) {
470 // We need to figure out the layer that follows newObject. We only do
471 // this the first time we find a child layer, and then we update the
472 // pointer values for newObject and beforeChild used by everyone else.
473 beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
474 newObject = nullptr;
475 }
476 parentLayer->addChild(obj->layer(), beforeChild);
477 return;
478 }
479
480 for (RenderObject *curr = obj->firstChild(); curr; curr = curr->nextSibling()) {
481 addLayers(curr, parentLayer, newObject, beforeChild);
482 }
483 }
484
addLayers(RenderLayer * parentLayer,RenderObject * newObject)485 void RenderObject::addLayers(RenderLayer *parentLayer, RenderObject *newObject)
486 {
487 if (!parentLayer) {
488 return;
489 }
490
491 RenderObject *object = newObject;
492 RenderLayer *beforeChild = nullptr;
493 ::addLayers(this, parentLayer, object, beforeChild);
494 }
495
removeLayers(RenderLayer * parentLayer)496 void RenderObject::removeLayers(RenderLayer *parentLayer)
497 {
498 if (!parentLayer) {
499 return;
500 }
501
502 if (layer()) {
503 parentLayer->removeChild(layer());
504 return;
505 }
506
507 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) {
508 curr->removeLayers(parentLayer);
509 }
510 }
511
moveLayers(RenderLayer * oldParent,RenderLayer * newParent)512 void RenderObject::moveLayers(RenderLayer *oldParent, RenderLayer *newParent)
513 {
514 if (!newParent) {
515 return;
516 }
517
518 if (layer()) {
519 if (oldParent) {
520 oldParent->removeChild(layer());
521 }
522 newParent->addChild(layer());
523 return;
524 }
525
526 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) {
527 curr->moveLayers(oldParent, newParent);
528 }
529 }
530
findNextLayer(RenderLayer * parentLayer,RenderObject * startPoint,bool checkParent)531 RenderLayer *RenderObject::findNextLayer(RenderLayer *parentLayer, RenderObject *startPoint,
532 bool checkParent)
533 {
534 // Error check the parent layer passed in. If it's null, we can't find anything.
535 if (!parentLayer) {
536 return nullptr;
537 }
538
539 // Step 1: If our layer is a child of the desired parent, then return our layer.
540 RenderLayer *ourLayer = layer();
541 if (ourLayer && ourLayer->parent() == parentLayer) {
542 return ourLayer;
543 }
544
545 // Step 2: If we don't have a layer, or our layer is the desired parent, then descend
546 // into our siblings trying to find the next layer whose parent is the desired parent.
547 if (!ourLayer || ourLayer == parentLayer) {
548 for (RenderObject *curr = startPoint ? startPoint->nextSibling() : firstChild();
549 curr; curr = curr->nextSibling()) {
550 RenderLayer *nextLayer = curr->findNextLayer(parentLayer, nullptr, false);
551 if (nextLayer) {
552 return nextLayer;
553 }
554 }
555 }
556
557 // Step 3: If our layer is the desired parent layer, then we're finished. We didn't
558 // find anything.
559 if (parentLayer == ourLayer) {
560 return nullptr;
561 }
562
563 // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that
564 // follow us to see if we can locate a layer.
565 if (checkParent && parent()) {
566 return parent()->findNextLayer(parentLayer, this, true);
567 }
568
569 return nullptr;
570 }
571
enclosingLayer() const572 RenderLayer *RenderObject::enclosingLayer() const
573 {
574 const RenderObject *curr = this;
575 while (curr) {
576 RenderLayer *layer = curr->layer();
577 if (layer) {
578 return layer;
579 }
580 curr = curr->parent();
581 }
582 return nullptr;
583 }
584
enclosingStackingContext() const585 RenderLayer *RenderObject::enclosingStackingContext() const
586 {
587 RenderLayer *l = enclosingLayer();
588 while (l && !l->isStackingContext()) {
589 l = l->parent();
590 }
591 return l;
592 }
593
clientRectToViewport(const QRectF & rect)594 QRectF RenderObject::clientRectToViewport(const QRectF &rect)
595 {
596 int offsetX = document()->part()->view()->contentsX();
597 int offsetY = document()->part()->view()->contentsY();
598
599 QRectF newRect(rect.x() - offsetX, rect.y() - offsetY,
600 rect.width(), rect.height());
601
602 return newRect;
603 }
604
getClientRects()605 QList<QRectF> RenderObject::getClientRects()
606 {
607 QList<QRectF> ret;
608
609 int x = 0;
610 int y = 0;
611 absolutePosition(x, y);
612
613 QRectF rect(x, y, width(), height());
614 ret.append(clientRectToViewport(rect));
615
616 return ret;
617 }
618
offsetLeft() const619 int RenderObject::offsetLeft() const
620 {
621 if (isBody()) {
622 return 0;
623 }
624
625 int x, dummy;
626 RenderObject *offsetPar = offsetParent();
627 if (!offsetPar || offsetPar->isBody()) {
628 if (style()->position() == PFIXED) {
629 return xPos();
630 } else {
631 absolutePosition(x, dummy);
632 return x;
633 }
634 }
635
636 x = xPos() - offsetPar->borderLeft();
637 if (isPositioned()) {
638 return x;
639 }
640
641 if (isRelPositioned()) {
642 int y = 0;
643 static_cast<const RenderBox *>(this)->relativePositionOffset(x, y);
644 }
645
646 for (RenderObject *curr = parent();
647 curr && curr != offsetPar;
648 curr = curr->parent()) {
649 x += curr->xPos();
650 }
651
652 return x;
653 }
654
offsetTop() const655 int RenderObject::offsetTop() const
656 {
657 if (isBody()) {
658 return 0;
659 }
660
661 int y, dummy;
662 RenderObject *offsetPar = offsetParent();
663 if (!offsetPar || offsetPar->isBody()) {
664 if (style()->position() == PFIXED) {
665 return yPos();
666 } else {
667 absolutePosition(dummy, y);
668 return y;
669 }
670 }
671
672 y = yPos() - offsetPar->borderTop();
673 if (isPositioned()) {
674 return y;
675 }
676
677 if (isRelPositioned()) {
678 int x = 0;
679 static_cast<const RenderBox *>(this)->relativePositionOffset(x, y);
680 }
681 for (RenderObject *curr = parent();
682 curr && curr != offsetPar;
683 curr = curr->parent()) {
684 y += curr->yPos();
685 }
686
687 return y;
688 }
689
offsetParent() const690 RenderObject *RenderObject::offsetParent() const
691 {
692 if (isBody() || style()->position() == PFIXED) {
693 return nullptr;
694 }
695
696 // can't really use containing blocks here (#113280)
697 bool skipTables = isPositioned() || isRelPositioned();
698 bool strict = !style()->htmlHacks();
699 RenderObject *curr = parent();
700 while (curr && (!curr->element() ||
701 (!curr->isPositioned() && !curr->isRelPositioned() &&
702 !(strict && skipTables ? curr->isRoot() : curr->isBody())))) {
703 if (!skipTables && curr->element() && (curr->isTableCell() || curr->isTable())) {
704 break;
705 }
706 curr = curr->parent();
707 }
708 return curr;
709 }
710
711 // IE extensions.
712 // clientWidth and clientHeight represent the interior of an object
clientWidth() const713 short RenderObject::clientWidth() const
714 {
715 return width() - borderLeft() - borderRight() -
716 (layer() ? layer()->verticalScrollbarWidth() : 0);
717 }
718
clientLeft() const719 int RenderObject::clientLeft() const
720 {
721 return borderLeft();
722 }
723
clientTop() const724 int RenderObject::clientTop() const
725 {
726 return borderTop();
727 }
728
clientHeight() const729 int RenderObject::clientHeight() const
730 {
731 return height() - borderTop() - borderBottom() -
732 (layer() ? layer()->horizontalScrollbarHeight() : 0);
733 }
734
735 // scrollWidth/scrollHeight is the size including the overflow area
scrollWidth() const736 short RenderObject::scrollWidth() const
737 {
738 return (hasOverflowClip() && layer()) ? layer()->scrollWidth() : overflowWidth() - overflowLeft();
739 }
740
scrollHeight() const741 int RenderObject::scrollHeight() const
742 {
743 return (hasOverflowClip() && layer()) ? layer()->scrollHeight() : overflowHeight() - overflowTop();
744 }
745
updatePixmap(const QRect &,CachedImage * image)746 void RenderObject::updatePixmap(const QRect & /*r*/, CachedImage *image)
747 {
748 #ifdef __GNUC__
749 #warning "FIXME: Check if complete!"
750 #endif
751 //repaint bg when it finished loading
752 if (image && parent() && style() && style()->backgroundLayers()->containsImage(image)) {
753 isBody() ? canvas()->repaint() : repaint();
754 }
755 }
756
setNeedsLayout(bool b,bool markParents)757 void RenderObject::setNeedsLayout(bool b, bool markParents)
758 {
759 bool alreadyNeededLayout = m_needsLayout;
760 m_needsLayout = b;
761 if (b) {
762 if (!alreadyNeededLayout && markParents && m_parent) {
763 dirtyFormattingContext(false);
764 markContainingBlocksForLayout();
765 }
766 } else {
767 m_posChildNeedsLayout = false;
768 m_normalChildNeedsLayout = false;
769 }
770 }
771
setChildNeedsLayout(bool b,bool markParents)772 void RenderObject::setChildNeedsLayout(bool b, bool markParents)
773 {
774 bool alreadyNeededLayout = m_normalChildNeedsLayout;
775 m_normalChildNeedsLayout = b;
776 if (b) {
777 if (!alreadyNeededLayout && markParents) {
778 markContainingBlocksForLayout();
779 }
780 } else {
781 m_posChildNeedsLayout = false;
782 m_normalChildNeedsLayout = false;
783 }
784 }
785
markContainingBlocksForLayout()786 void RenderObject::markContainingBlocksForLayout()
787 {
788 RenderObject *o = container();
789 RenderObject *last = this;
790
791 while (o) {
792 if (!last->isText() && (last->style()->position() == PFIXED || last->style()->position() == PABSOLUTE)) {
793 if (o->m_posChildNeedsLayout) {
794 return;
795 }
796 o->m_posChildNeedsLayout = true;
797 } else {
798 if (o->m_normalChildNeedsLayout) {
799 return;
800 }
801 o->m_normalChildNeedsLayout = true;
802 }
803
804 last = o;
805 o = o->container();
806 }
807
808 last->scheduleRelayout();
809 }
810
containingBlock() const811 RenderBlock *RenderObject::containingBlock() const
812 {
813 if (isTableCell()) {
814 return static_cast<RenderBlock *>(parent()->parent()->parent());
815 }
816 if (isCanvas()) {
817 return const_cast<RenderBlock *>(static_cast<const RenderBlock *>(this));
818 }
819
820 RenderObject *o = parent();
821 if (m_style->position() == PFIXED) {
822 while (o && !o->isCanvas()) {
823 o = o->parent();
824 }
825 } else if (m_style->position() == PABSOLUTE) {
826 while (o &&
827 (o->style()->position() == PSTATIC || (o->isInline() && !o->isReplaced())) && !o->isCanvas()) {
828 // for relpos inlines, return the nearest block - it will host the positioned objects list
829 if (o->isInline() && !o->isReplaced() && o->style()->position() == PRELATIVE) {
830 return o->containingBlock();
831 }
832 o = o->parent();
833 }
834 } else {
835 while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection() ||
836 o->isTableCol() || o->isFrameSet() ||
837 o->isSVGContainer() || o->isSVGRoot())) // for svg
838
839 {
840 o = o->parent();
841 }
842 }
843 // this is just to make sure we return a valid element.
844 // the case below should never happen...
845 if (!o || !o->isRenderBlock()) {
846 if (!isCanvas()) {
847 #ifndef NDEBUG
848 qCDebug(KHTML_LOG) << this << ": " << renderName() << "(RenderObject): No containingBlock!";
849 const RenderObject *p = this;
850 while (p->parent()) {
851 p = p->parent();
852 }
853 p->printTree();
854 #endif
855 }
856 return canvas(); // likely wrong, but better than a crash
857 }
858
859 return static_cast<RenderBlock *>(o);
860 }
861
containingBlockWidth(RenderObject *) const862 short RenderObject::containingBlockWidth(RenderObject *) const
863 {
864 // ###
865 return containingBlock()->contentWidth();
866 }
867
containingBlockHeight(RenderObject *) const868 int RenderObject::containingBlockHeight(RenderObject *) const
869 {
870 // ###
871 return containingBlock()->contentHeight();
872 }
873
sizesToMaxWidth() const874 bool RenderObject::sizesToMaxWidth() const
875 {
876 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
877 // but they allow text to sit on the same line as the marquee.
878 if (isFloating() || isCompact() ||
879 (isInlineBlockOrInlineTable() && !isHTMLMarquee()) ||
880 (element() && (element()->id() == ID_BUTTON || element()->id() == ID_LEGEND))) {
881 return true;
882 }
883
884 // Children of a horizontal marquee do not fill the container by default.
885 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
886 if (parent()->style()->overflowX() == OMARQUEE) {
887 EMarqueeDirection dir = parent()->style()->marqueeDirection();
888 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) {
889 return true;
890 }
891 }
892
893 #ifdef APPLE_CHANGES // ### what the heck is a flexbox?
894 // Flexible horizontal boxes lay out children at their maxwidths. Also vertical boxes
895 // that don't stretch their kids lay out their children at their maxwidths.
896 if (parent()->isFlexibleBox() &&
897 (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) {
898 return true;
899 }
900 #endif
901
902 return false;
903 }
904
905 // from Mozilla's nsCSSColorUtils.cpp
brightness(int red,int green,int blue)906 static int brightness(int red, int green, int blue)
907 {
908
909 int intensity = (red + green + blue) / 3;
910
911 int luminosity =
912 ((RED_LUMINOSITY * red) / 100) +
913 ((GREEN_LUMINOSITY * green) / 100) +
914 ((BLUE_LUMINOSITY * blue) / 100);
915
916 return ((intensity * INTENSITY_FACTOR) +
917 (luminosity * LUMINOSITY_FACTOR)) / 100;
918 }
919
calc3DColor(QColor & color,bool darken)920 static void calc3DColor(QColor &color, bool darken)
921 {
922 int rb = color.red();
923 int gb = color.green();
924 int bb = color.blue();
925 int a = color.alpha();
926
927 int brightness_ = brightness(rb, gb, bb);
928
929 int f0, f1;
930 if (brightness_ < COLOR_DARK_THRESHOLD) {
931 f0 = COLOR_DARK_BS_FACTOR;
932 f1 = COLOR_DARK_TS_FACTOR;
933 } else if (brightness_ > COLOR_LIGHT_THRESHOLD) {
934 f0 = COLOR_LITE_BS_FACTOR;
935 f1 = COLOR_LITE_TS_FACTOR;
936 } else {
937 f0 = COLOR_DARK_BS_FACTOR +
938 (brightness_ *
939 (COLOR_LITE_BS_FACTOR - COLOR_DARK_BS_FACTOR) / MAX_COLOR);
940 f1 = COLOR_DARK_TS_FACTOR +
941 (brightness_ *
942 (COLOR_LITE_TS_FACTOR - COLOR_DARK_TS_FACTOR) / MAX_COLOR);
943 }
944
945 if (darken) {
946 int r = rb - (f0 * rb / 100);
947 int g = gb - (f0 * gb / 100);
948 int b = bb - (f0 * bb / 100);
949 if ((r == rb) && (g == gb) && (b == bb)) {
950 color = (color == Qt::black) ? QColor(DARK_GRAY) : QColor(Qt::black);
951 } else {
952 color.setRgb(r, g, b);
953 }
954 } else {
955 int r = qMin(rb + (f1 * (MAX_COLOR - rb) / 100), 255);
956 int g = qMin(gb + (f1 * (MAX_COLOR - gb) / 100), 255);
957 int b = qMin(bb + (f1 * (MAX_COLOR - bb) / 100), 255);
958 if ((r == rb) && (g == gb) && (b == bb)) {
959 color = (color == Qt::white) ? QColor(LIGHT_GRAY) : QColor(Qt::white);
960 } else {
961 color.setRgb(r, g, b);
962 }
963 }
964 color.setAlpha(a);
965 }
966
drawBorder(QPainter * p,int x1,int y1,int x2,int y2,BorderSide s,QColor c,const QColor & textcolor,EBorderStyle style,int adjbw1,int adjbw2,bool invalidisInvert,qreal * nextDashOffset)967 void RenderObject::drawBorder(QPainter *p, int x1, int y1, int x2, int y2,
968 BorderSide s, QColor c, const QColor &textcolor, EBorderStyle style,
969 int adjbw1, int adjbw2, bool invalidisInvert, qreal *nextDashOffset)
970 {
971 if (nextDashOffset && style != DOTTED && style != DASHED) {
972 *nextDashOffset = 0;
973 }
974
975 if (p->hasClipping() && !p->clipRegion().boundingRect().intersects(QRect(x1, y1, x2 - x1, y2 - y1))) {
976 if (nextDashOffset && (style == DOTTED || style == DASHED)) {
977 *nextDashOffset += (s == BSTop || s == BSBottom) ? (x2 - x1) : (y2 - y1);
978 }
979 return;
980 }
981
982 int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1);
983
984 if (style == DOUBLE && width < 3) {
985 style = SOLID;
986 }
987
988 if (!c.isValid()) {
989 if (invalidisInvert) {
990 // handle 'outline-color: invert'
991 if (p->paintEngine() && p->paintEngine()->hasFeature(QPaintEngine::BlendModes)) {
992 p->setCompositionMode(QPainter::CompositionMode_Difference);
993 c = Qt::white;
994 } else {
995 // 'invert' is not supported on this platform (for instance XRender)
996 // CSS3 UI 8.4: If the UA does not support the 'invert' value then the initial value of
997 // the 'outline-color' property is the 'currentColor' [CSS3COLOR] keyword.
998 c = m_style->color();
999 }
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
calcBorderRadii(QPoint & topLeftRadii,QPoint & topRightRadii,QPoint & bottomLeftRadii,QPoint & bottomRightRadii,int w,int h) const1312 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
blendCornerImages(const QImage & image1,const QImage & image2)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
cornerGradient(int cx,int cy,const QPoint & radius,int angleStart,int angleSpan,const QColor & startColor,const QColor & finalColor)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
drawBorderArc(QPainter * p,int x,int y,float horThickness,float vertThickness,const QPoint & radius,int angleStart,int angleSpan,const QBrush & brush,const QColor & textColor,EBorderStyle style,qreal * nextDashOffset) const1422 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
paintBorder(QPainter * p,int _tx,int _ty,int w,int h,const RenderStyle * style,bool begin,bool end)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
paintOutline(QPainter * p,int _tx,int _ty,int w,int h,const RenderStyle * style)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
paint(PaintInfo &,int,int)1869 void RenderObject::paint(PaintInfo &, int /*tx*/, int /*ty*/)
1870 {
1871 }
1872
repaintRectangle(int x,int y,int w,int h,Priority p,bool f)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
information() const1882 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
printTree(int indent) const1988 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
operator <<(QTextStream & ts,const QRect & r)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.
lookupTagName(NodeImpl * node)2008 static QString lookupTagName(NodeImpl *node)
2009 {
2010 return LocalName::fromId(node->id()).toString().string();
2011 }
2012
dump(QTextStream & ts,const QString & ind) const2013 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
printLineBoxTree() const2091 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
shouldSelect() const2107 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
selectionStartEnd(int & spos,int & epos)2137 void RenderObject::selectionStartEnd(int &spos, int &epos)
2138 {
2139 if (parent()) {
2140 parent()->selectionStartEnd(spos, epos);
2141 }
2142 }
2143
setStyle(RenderStyle * style)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
attemptDirectLayerTranslation()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
dirtyFormattingContext(bool checkContainer)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
repaintDuringLayout()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
updateBackgroundImages(RenderStyle * oldStyle)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
viewRect() const2380 QRect RenderObject::viewRect() const
2381 {
2382 return containingBlock()->viewRect();
2383 }
2384
absolutePosition(int & xPos,int & yPos,bool f) const2385 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
caretPos(int,int,int & _x,int & _y,int & width,int & height) const2400 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
paddingTop() const2407 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
paddingBottom() const2421 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
paddingLeft() const2435 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
paddingRight() const2449 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
container() const2463 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
document() const2501 DOM::DocumentImpl *RenderObject::document() const
2502 {
2503 return m_node->document();
2504 }
2505
removeFromObjectLists()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
renderArena() const2546 RenderArena *RenderObject::renderArena() const
2547 {
2548 return m_node->document()->renderArena();
2549 }
2550
detach()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
remove()2565 void RenderObject::remove()
2566 {
2567 if (m_parent) {
2568 m_parent->removeChild(this);
2569 if (isFloating() || inPosObjectList()) {
2570 removeFromObjectLists();
2571 }
2572 }
2573 }
2574
arenaDelete(RenderArena * arena,void * base)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
arenaDelete(RenderArena * arena)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
positionForCoordinates(int,int)2597 RenderPosition RenderObject::positionForCoordinates(int /*x*/, int /*y*/)
2598 {
2599 return RenderPosition(element(), caretMinOffset());
2600 }
2601
isPointInsideSelection(int x,int y,const Selection & sel) const2602 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
mouseInside() const2700 bool RenderObject::mouseInside() const
2701 {
2702 if (!m_mouseInside && continuation()) {
2703 return continuation()->mouseInside();
2704 }
2705 return m_mouseInside;
2706 }
2707
nodeAtPoint(NodeInfo & info,int _x,int _y,int _tx,int _ty,HitTestAction hitTestAction,bool inside)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
setInnerNode(NodeInfo & info)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
verticalPositionHint(bool firstLine) const2769 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
getVerticalPosition(bool firstLine,RenderObject * ref) const2782 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
lineHeight(bool firstLine) const2831 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
baselinePosition(bool firstLine) const2865 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
invalidateVerticalPosition()2877 void RenderObject::invalidateVerticalPosition()
2878 {
2879 m_verticalPosition = PositionUndefined;
2880 }
2881
recalcMinMaxWidths()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
scheduleRelayout(RenderObject * clippedObj)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
createInlineBox(bool,bool)2936 InlineBox *RenderObject::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
2937 {
2938 KHTMLAssert(false);
2939 return nullptr;
2940 }
2941
getTextDecorationColors(int decorations,QColor & underline,QColor & overline,QColor & linethrough,bool quirksMode)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
maximalOutlineSize(PaintAction p) const2985 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
collectBorders(QList<CollapsedBorderValue> & borderStyles)2993 void RenderObject::collectBorders(QList<CollapsedBorderValue> &borderStyles)
2994 {
2995 for (RenderObject *curr = firstChild(); curr; curr = curr->nextSibling()) {
2996 curr->collectBorders(borderStyles);
2997 }
2998 }
2999
flowAroundFloats() const3000 bool RenderObject::flowAroundFloats() const
3001 {
3002 return isReplaced() || hasOverflowClip() || style()->flowAroundFloats();
3003 }
3004
usesLineWidth() const3005 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
caretMinOffset() const3013 long RenderObject::caretMinOffset() const
3014 {
3015 return 0;
3016 }
3017
caretMaxOffset() const3018 long RenderObject::caretMaxOffset() const
3019 {
3020 return 0;
3021 }
3022
caretMaxRenderedOffset() const3023 unsigned long RenderObject::caretMaxRenderedOffset() const
3024 {
3025 return 0;
3026 }
3027
inlineBox(long)3028 InlineBox *RenderObject::inlineBox(long /*offset*/)
3029 {
3030 if (isBox()) {
3031 return static_cast<RenderBox *>(this)->placeHolderBox();
3032 }
3033 return nullptr;
3034 }
3035
hasCounter(const DOMString & counter) const3036 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
getCounter(const DOMString & counter,bool view,bool counters)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
lookupCounter(const DOMString & counter) const3203 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
detachCounters()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
insertCounter(const DOMString & counter,CounterNode * val)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
updateWidgetMasks()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
visibleFlowRegion(int x,int y) const3289 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
relativeBBox(bool includeStroke) const3321 FloatRect RenderObject::relativeBBox(bool includeStroke) const
3322 {
3323 Q_UNUSED(includeStroke);
3324 return FloatRect();
3325 }
3326
localTransform() const3327 AffineTransform RenderObject::localTransform() const
3328 {
3329 return AffineTransform(1, 0, 0, 1, xPos(), yPos());
3330 }
3331
absoluteTransform() const3332 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
3361