1 /*
2 * Copyright (C) 2003 Apple Computer, Inc.
3 * (C) 2006 Germain Garand <germain@ebooksfrance.org>
4 * (C) 2006 Allan Sandfeld Jense <kde@carewolf.com>
5 *
6 * Portions are Copyright (C) 1998 Netscape Communications Corporation.
7 *
8 * Other contributors:
9 * Robert O'Callahan <roc+@cs.cmu.edu>
10 * David Baron <dbaron@fas.harvard.edu>
11 * Christian Biesinger <cbiesinger@web.de>
12 * Randall Jesup <rjesup@wgate.com>
13 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
14 * Josh Soref <timeless@mac.com>
15 * Boris Zbarsky <bzbarsky@mit.edu>
16 *
17 * This library is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Lesser General Public
19 * License as published by the Free Software Foundation; either
20 * version 2.1 of the License, or (at your option) any later version.
21 *
22 * This library is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 * Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public
28 * License along with this library; if not, write to the Free Software
29 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30 *
31 * Alternatively, the contents of this file may be used under the terms
32 * of either the Mozilla Public License Version 1.1, found at
33 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
34 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
35 * (the "GPL"), in which case the provisions of the MPL or the GPL are
36 * applicable instead of those above. If you wish to allow use of your
37 * version of this file only under the terms of one of those two
38 * licenses (the MPL or the GPL) and not to allow others to use your
39 * version of this file under the LGPL, indicate your decision by
40 * deletingthe provisions above and replace them with the notice and
41 * other provisions required by the MPL or the GPL, as the case may be.
42 * If you do not delete the provisions above, a recipient may use your
43 * version of this file under any of the LGPL, the MPL or the GPL.
44 */
45
46 //#define BOX_DEBUG
47
48 #include "render_layer.h"
49 #include "khtmlview.h"
50 #include "render_canvas.h"
51 #include "render_arena.h"
52 #include "render_replaced.h"
53 #include "render_form.h"
54 #include "xml/dom_docimpl.h"
55 #include "xml/dom2_eventsimpl.h"
56 #include "misc/paintbuffer.h"
57 #include "html/html_blockimpl.h"
58 #include "xml/dom_restyler.h"
59
60 #include <QStyle>
61 #include <QStack>
62
63 using namespace DOM;
64 using namespace khtml;
65
66 ScrollBarWidget *RenderLayer::gScrollBar = nullptr;
67
68 #ifndef NDEBUG
69 static bool inRenderLayerDetach;
70 #endif
71
72 void
slotValueChanged()73 RenderScrollMediator::slotValueChanged()
74 {
75 if (m_layer->renderer()->canvas()->isPerformingLayout()) {
76 if (!m_waitingForUpdate) {
77 QTimer::singleShot(0, this, SLOT(slotValueChanged()));
78 }
79 m_waitingForUpdate = true;
80 } else {
81 m_waitingForUpdate = false;
82 m_layer->updateScrollPositionFromScrollbars();
83 }
84 }
85
RenderLayer(RenderObject * object)86 RenderLayer::RenderLayer(RenderObject *object)
87 : m_object(object),
88 m_parent(nullptr),
89 m_previous(nullptr),
90 m_next(nullptr),
91 m_first(nullptr),
92 m_last(nullptr),
93 m_x(0),
94 m_y(0),
95 m_scrollX(0),
96 m_scrollY(0),
97 m_scrollXOrigin(0),
98 m_scrollWidth(0),
99 m_scrollHeight(0),
100 m_hBar(nullptr),
101 m_vBar(nullptr),
102 m_scrollMediator(nullptr),
103 m_posZOrderList(nullptr),
104 m_negZOrderList(nullptr),
105 m_overflowList(nullptr),
106 m_zOrderListsDirty(true),
107 m_overflowListDirty(true),
108 m_isOverflowOnly(shouldBeOverflowOnly()),
109 m_markedForRepaint(false),
110 m_hasOverlaidWidgets(false),
111 m_visibleContentStatusDirty(true),
112 m_hasVisibleContent(false),
113 m_visibleDescendantStatusDirty(false),
114 m_hasVisibleDescendant(false),
115 m_inScrollbarRelayout(false),
116 m_marquee(nullptr)
117 {
118 if (!object->firstChild() && object->style()) {
119 m_visibleContentStatusDirty = false;
120 m_hasVisibleContent = object->style()->visibility() == VISIBLE;
121 }
122 m_buffer[0] = nullptr;
123 m_buffer[1] = nullptr;
124 m_wasStackingContext = object->style() ? isStackingContext() : false;
125 }
126
~RenderLayer()127 RenderLayer::~RenderLayer()
128 {
129 // Child layers will be deleted by their corresponding render objects, so
130 // our destructor doesn't have to do anything.
131 delete m_hBar;
132 delete m_vBar;
133 delete m_buffer[0];
134 delete m_buffer[1];
135 delete m_scrollMediator;
136 delete m_posZOrderList;
137 delete m_negZOrderList;
138 delete m_overflowList;
139 delete m_marquee;
140 }
141
updateLayerPosition()142 void RenderLayer::updateLayerPosition()
143 {
144
145 // The canvas is sized to the docWidth/Height over in RenderCanvas::layout, so we
146 // don't need to ever update our layer position here.
147 if (renderer()->isCanvas()) {
148 return;
149 }
150
151 int x = m_object->xPos();
152 int y = m_object->yPos() - m_object->borderTopExtra();
153
154 if (!m_object->isPositioned()) {
155 // We must adjust our position by walking up the render tree looking for the
156 // nearest enclosing object with a layer.
157 RenderObject *curr = m_object->parent();
158 while (curr && !curr->layer()) {
159 x += curr->xPos();
160 y += curr->yPos();
161 curr = curr->parent();
162 }
163 if (curr) {
164 y += curr->borderTopExtra();
165 }
166 }
167
168 if (m_object->isRelPositioned()) {
169 static_cast<RenderBox *>(m_object)->relativePositionOffset(x, y);
170 }
171
172 // Subtract our parent's scroll offset.
173 if (m_object->isPositioned() && enclosingPositionedAncestor()) {
174 RenderLayer *positionedParent = enclosingPositionedAncestor();
175
176 // For positioned layers, we subtract out the enclosing positioned layer's scroll offset.
177 positionedParent->subtractScrollOffset(x, y);
178 positionedParent->checkInlineRelOffset(m_object, x, y);
179 } else if (parent()) {
180 parent()->subtractScrollOffset(x, y);
181 }
182
183 setPos(x, y);
184 }
185
paintedRegion(RenderLayer * rootLayer)186 QRegion RenderLayer::paintedRegion(RenderLayer *rootLayer)
187 {
188 updateZOrderLists();
189 QRegion r;
190 const RenderStyle *s = renderer()->style();
191 bool isTrans = (s->opacity() < 1.0);
192 if (isTrans && m_hasVisibleDescendant) {
193 if (!s->opacity()) {
194 return r;
195 }
196 for (RenderLayer *ch = firstChild(); ch; ch = ch->nextSibling()) {
197 r += ch->paintedRegion(rootLayer);
198 }
199 } else if (m_negZOrderList && m_hasVisibleDescendant) {
200 uint count = m_negZOrderList->count();
201 for (uint i = 0; i < count; i++) {
202 RenderLayer *child = m_negZOrderList->at(i);
203 r += child->paintedRegion(rootLayer);
204 }
205 }
206
207 if (m_hasVisibleContent) {
208 int x = 0; int y = 0;
209 convertToLayerCoords(rootLayer, x, y);
210 QRect cr(x, y, width(), height());
211 if (s->visibility() == VISIBLE && (s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() ||
212 renderer()->scrollsOverflow() || renderer()->isReplaced())) {
213 if (!s->hidesOverflow()) {
214 r += renderer()->visibleFlowRegion(x, y);
215 }
216 r += cr;
217 } else {
218 r += renderer()->visibleFlowRegion(x, y);
219 }
220 }
221
222 if (!isTrans && m_posZOrderList && m_hasVisibleDescendant) {
223 uint count = m_posZOrderList->count();
224 for (uint i = 0; i < count; i++) {
225 RenderLayer *child = m_posZOrderList->at(i);
226 r += child->paintedRegion(rootLayer);
227 }
228 }
229 return r;
230 }
231
repaint(Priority p,bool markForRepaint)232 void RenderLayer::repaint(Priority p, bool markForRepaint)
233 {
234 if (markForRepaint && m_markedForRepaint) {
235 return;
236 }
237 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
238 child->repaint(p, markForRepaint);
239 }
240 QRect layerBounds, damageRect, fgrect;
241 calculateRects(renderer()->canvas()->layer(), renderer()->viewRect(), layerBounds, damageRect, fgrect);
242 m_visibleRect = damageRect.intersected(layerBounds);
243 if (m_visibleRect.isValid()) {
244 renderer()->canvas()->repaintViewRectangle(m_visibleRect.x(), m_visibleRect.y(), m_visibleRect.width(), m_visibleRect.height(), (p > NormalPriority));
245 }
246 if (markForRepaint) {
247 m_markedForRepaint = true;
248 }
249 }
250
updateLayerPositions(RenderLayer * rootLayer,bool doFullRepaint,bool checkForRepaint)251 void RenderLayer::updateLayerPositions(RenderLayer *rootLayer, bool doFullRepaint, bool checkForRepaint)
252 {
253 if (doFullRepaint) {
254 m_object->repaint();
255 checkForRepaint = doFullRepaint = false;
256 }
257
258 updateLayerPosition(); // For relpositioned layers or non-positioned layers,
259 // we need to keep in sync, since we may have shifted relative
260 // to our parent layer.
261
262 if (m_hBar || m_vBar) {
263 // Need to position the scrollbars.
264 int x = 0;
265 int y = 0;
266 convertToLayerCoords(rootLayer, x, y);
267 QRect layerBounds = QRect(x, y, width(), height());
268 positionScrollbars(layerBounds);
269 }
270
271 updateVisibilityStatus();
272
273 if (m_hasVisibleContent && checkForRepaint && m_markedForRepaint) {
274 QRect layerBounds, damageRect, fgrect;
275 calculateRects(rootLayer, renderer()->viewRect(), layerBounds, damageRect, fgrect);
276 QRect vr = damageRect.intersected(layerBounds);
277 if (vr != m_visibleRect && vr.isValid()) {
278 renderer()->canvas()->repaintViewRectangle(vr.x(), vr.y(), vr.width(), vr.height());
279 m_visibleRect = vr;
280 }
281 }
282 m_markedForRepaint = false;
283
284 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
285 child->updateLayerPositions(rootLayer, doFullRepaint, checkForRepaint);
286 }
287
288 // With all our children positioned, now update our marquee if we need to.
289 if (m_marquee) {
290 m_marquee->updateMarqueePosition();
291 }
292 }
293
setHasVisibleContent(bool b)294 void RenderLayer::setHasVisibleContent(bool b)
295 {
296 if (m_hasVisibleContent == b && !m_visibleContentStatusDirty) {
297 return;
298 }
299 m_visibleContentStatusDirty = false;
300 m_hasVisibleContent = b;
301 if (m_hasVisibleContent) {
302 // ### dirty painted region
303 // m_region = QRegion();
304 if (!isOverflowOnly())
305 if (RenderLayer *sc = stackingContext()) {
306 sc->dirtyZOrderLists();
307 }
308 }
309 if (parent()) {
310 parent()->childVisibilityChanged(m_hasVisibleContent);
311 }
312 }
313
dirtyVisibleContentStatus()314 void RenderLayer::dirtyVisibleContentStatus()
315 {
316 m_visibleContentStatusDirty = true;
317 if (parent()) {
318 parent()->dirtyVisibleDescendantStatus();
319 }
320 }
321
childVisibilityChanged(bool newVisibility)322 void RenderLayer::childVisibilityChanged(bool newVisibility)
323 {
324 if (m_hasVisibleDescendant == newVisibility || m_visibleDescendantStatusDirty) {
325 return;
326 }
327 if (newVisibility) {
328 RenderLayer *l = this;
329 while (l && !l->m_visibleDescendantStatusDirty && !l->m_hasVisibleDescendant) {
330 l->m_hasVisibleDescendant = true;
331 l = l->parent();
332 }
333 } else {
334 dirtyVisibleDescendantStatus();
335 }
336 }
337
dirtyVisibleDescendantStatus()338 void RenderLayer::dirtyVisibleDescendantStatus()
339 {
340 RenderLayer *l = this;
341 while (l && !l->m_visibleDescendantStatusDirty) {
342 l->m_visibleDescendantStatusDirty = true;
343 l = l->parent();
344 }
345 }
346
updateVisibilityStatus()347 void RenderLayer::updateVisibilityStatus()
348 {
349 if (m_visibleDescendantStatusDirty) {
350 m_hasVisibleDescendant = false;
351 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
352 child->updateVisibilityStatus();
353 if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) {
354 m_hasVisibleDescendant = true;
355 break;
356 }
357 }
358 m_visibleDescendantStatusDirty = false;
359 }
360
361 if (m_visibleContentStatusDirty) {
362 if (m_object->style()->visibility() == VISIBLE) {
363 m_hasVisibleContent = true;
364 } else {
365 // layer may be hidden but still have some visible content, check for this
366 m_hasVisibleContent = false;
367 RenderObject *r = m_object->firstChild();
368 while (r) {
369 if (r->style()->visibility() == VISIBLE && !r->layer()) {
370 m_hasVisibleContent = true;
371 break;
372 }
373 if (r->firstChild() && !r->layer()) {
374 r = r->firstChild();
375 } else if (r->nextSibling()) {
376 r = r->nextSibling();
377 } else {
378 do {
379 r = r->parent();
380 if (r == m_object) {
381 r = nullptr;
382 }
383 } while (r && !r->nextSibling());
384 if (r) {
385 r = r->nextSibling();
386 }
387 }
388 }
389 }
390 m_visibleContentStatusDirty = false;
391 }
392 }
393
updateWidgetMasks(RenderLayer * rootLayer)394 void RenderLayer::updateWidgetMasks(RenderLayer *rootLayer)
395 {
396 if (hasOverlaidWidgets() && !renderer()->canvas()->pagedMode()) {
397 updateZOrderLists();
398 uint count = m_posZOrderList ? m_posZOrderList->count() : 0;
399 bool needUpdate = false;
400 KHTMLView *sa = nullptr;
401 if (count > 0) {
402 sa = m_object->document()->view();
403 m_region = QRect(0, 0, sa->contentsWidth(), sa->contentsHeight());
404 for (uint i = 0; i < count; i++) {
405 RenderLayer *child = m_posZOrderList->at(i);
406 if (child->zIndex() == 0 && child->renderer()->style()->position() == PSTATIC) {
407 continue; // we don't know the widget's exact stacking position within flow
408 }
409 m_region -= child->paintedRegion(rootLayer);
410 }
411 needUpdate = true;
412 }
413 RenderLayer *sc = this;
414 int zx = zIndex();
415 while ((sc = sc->stackingContext())) {
416 sc->updateZOrderLists();
417 bool found = false;
418 if (zx < 0) {
419 count = sc->m_negZOrderList ? sc->m_negZOrderList->count() : 0;
420 needUpdate = needUpdate || count > 0;
421 for (uint i = 0; i < count; i++) {
422 found = found || sc->m_negZOrderList->at(i)->zIndex() > zx;
423 if (found) {
424 if (!sa) {
425 sa = m_object->document()->view();
426 m_region = QRect(0, 0, sa->contentsWidth(), sa->contentsHeight());
427 }
428 m_region -= sc->m_negZOrderList->at(i)->paintedRegion(rootLayer);
429 }
430 }
431 }
432 count = sc->m_posZOrderList ? sc->m_posZOrderList->count() : 0;
433 if (count > 0) {
434 needUpdate = true;
435 for (uint i = 0; i < count; i++) {
436 found = found || sc->m_posZOrderList->at(i)->zIndex() > zx;
437 if (found) {
438 if (!sa) {
439 sa = m_object->document()->view();
440 m_region = QRect(0, 0, sa->contentsWidth(), sa->contentsHeight());
441 }
442 m_region -= sc->m_posZOrderList->at(i)->paintedRegion(rootLayer);
443 }
444 }
445 }
446 zx = sc->zIndex();
447 }
448 if (!needUpdate) {
449 needUpdate = !m_region.isEmpty();
450 m_region = QRegion();
451 }
452 if (needUpdate) {
453 renderer()->updateWidgetMasks();
454 }
455 }
456 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
457 child->updateWidgetMasks(rootLayer);
458 }
459 }
460
width() const461 int RenderLayer::width() const
462 {
463 int w = m_object->width();
464 if (!m_object->hasOverflowClip()) {
465 w = qMax(m_object->overflowWidth(), w);
466 }
467 return w;
468 }
469
height() const470 int RenderLayer::height() const
471 {
472 int h = m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra();
473 if (!m_object->hasOverflowClip()) {
474 h = qMax(m_object->overflowHeight(), h);
475 }
476 return h;
477 }
478
stackingContext() const479 RenderLayer *RenderLayer::stackingContext() const
480 {
481 RenderLayer *curr = parent();
482 for (; curr && !curr->m_object->isCanvas() &&
483 curr->m_object->style()->hasAutoZIndex();
484 curr = curr->parent()) {};
485 return curr;
486 }
487
enclosingPositionedAncestor() const488 RenderLayer *RenderLayer::enclosingPositionedAncestor() const
489 {
490 RenderLayer *curr = parent();
491 for (; curr && !curr->m_object->isCanvas() &&
492 !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned();
493 curr = curr->parent()) {};
494
495 return curr;
496 }
497
isTransparent() const498 bool RenderLayer::isTransparent() const
499 {
500 return m_object->style()->opacity() < 1.0f;
501 }
502
transparentAncestor() const503 RenderLayer *RenderLayer::transparentAncestor() const
504 {
505 RenderLayer *curr = parent();
506 for (; curr && curr->m_object->style()->opacity() == 1.0f; curr = curr->parent()) {};
507 return curr;
508 }
509
operator new(size_t sz,RenderArena * renderArena)510 void *RenderLayer::operator new(size_t sz, RenderArena *renderArena) throw()
511 {
512 return renderArena->allocate(sz);
513 }
514
operator delete(void * ptr,size_t sz)515 void RenderLayer::operator delete(void *ptr, size_t sz)
516 {
517 assert(inRenderLayerDetach);
518 #ifdef KHTML_USE_ARENA_ALLOCATOR
519 // Stash size where detach can find it.
520 *(size_t *)ptr = sz;
521 #endif
522 }
523
detach(RenderArena * renderArena)524 void RenderLayer::detach(RenderArena *renderArena)
525 {
526 #ifndef NDEBUG
527 inRenderLayerDetach = true;
528 #endif
529 delete this;
530 #ifndef NDEBUG
531 inRenderLayerDetach = false;
532 #endif
533
534 // Recover the size left there for us by operator delete and free the memory.
535 renderArena->free(*(size_t *)this, this);
536 }
537
addChild(RenderLayer * child,RenderLayer * beforeChild)538 void RenderLayer::addChild(RenderLayer *child, RenderLayer *beforeChild)
539 {
540 RenderLayer *prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
541 if (prevSibling) {
542 child->setPreviousSibling(prevSibling);
543 prevSibling->setNextSibling(child);
544 } else {
545 setFirstChild(child);
546 }
547
548 if (beforeChild) {
549 beforeChild->setPreviousSibling(child);
550 child->setNextSibling(beforeChild);
551 } else {
552 setLastChild(child);
553 }
554
555 child->setParent(this);
556
557 if (child->isOverflowOnly()) {
558 dirtyOverflowList();
559 } else {
560 // Dirty the z-order list in which we are contained. The stackingContext() can be null in the
561 // case where we're building up generated content layers. This is ok, since the lists will start
562 // off dirty in that case anyway.
563 RenderLayer *stackingContext = child->stackingContext();
564 if (stackingContext) {
565 stackingContext->dirtyZOrderLists();
566 }
567 }
568 child->updateVisibilityStatus();
569 if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) {
570 childVisibilityChanged(true);
571 }
572 }
573
removeChild(RenderLayer * oldChild)574 RenderLayer *RenderLayer::removeChild(RenderLayer *oldChild)
575 {
576 // remove the child
577 if (oldChild->previousSibling()) {
578 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
579 }
580 if (oldChild->nextSibling()) {
581 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
582 }
583
584 if (m_first == oldChild) {
585 m_first = oldChild->nextSibling();
586 }
587 if (m_last == oldChild) {
588 m_last = oldChild->previousSibling();
589 }
590
591 if (oldChild->isOverflowOnly()) {
592 dirtyOverflowList();
593 } else {
594 // Dirty the z-order list in which we are contained. When called via the
595 // reattachment process in removeOnlyThisLayer, the layer may already be disconnected
596 // from the main layer tree, so we need to null-check the |stackingContext| value.
597 RenderLayer *stackingContext = oldChild->stackingContext();
598 if (stackingContext) {
599 stackingContext->dirtyZOrderLists();
600 }
601 }
602
603 oldChild->setPreviousSibling(nullptr);
604 oldChild->setNextSibling(nullptr);
605 oldChild->setParent(nullptr);
606
607 oldChild->updateVisibilityStatus();
608 if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) {
609 childVisibilityChanged(false);
610 }
611
612 return oldChild;
613 }
614
removeOnlyThisLayer()615 void RenderLayer::removeOnlyThisLayer()
616 {
617 if (!m_parent) {
618 return;
619 }
620
621 // Remove us from the parent.
622 RenderLayer *parent = m_parent;
623 RenderLayer *nextSib = nextSibling();
624 parent->removeChild(this);
625
626 // Now walk our kids and reattach them to our parent.
627 RenderLayer *current = m_first;
628 while (current) {
629 RenderLayer *next = current->nextSibling();
630 removeChild(current);
631 parent->addChild(current, nextSib);
632 current = next;
633 }
634
635 detach(renderer()->renderArena());
636 }
637
insertOnlyThisLayer()638 void RenderLayer::insertOnlyThisLayer()
639 {
640 if (!m_parent && renderer()->parent()) {
641 // We need to connect ourselves when our renderer() has a parent.
642 // Find our enclosingLayer and add ourselves.
643 RenderLayer *parentLayer = renderer()->parent()->enclosingLayer();
644 if (parentLayer)
645 parentLayer->addChild(this,
646 renderer()->parent()->findNextLayer(parentLayer, renderer()));
647 }
648
649 // Remove all descendant layers from the hierarchy and add them to the new position.
650 for (RenderObject *curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) {
651 curr->moveLayers(m_parent, this);
652 }
653 }
654
convertToLayerCoords(const RenderLayer * ancestorLayer,int & x,int & y) const655 void RenderLayer::convertToLayerCoords(const RenderLayer *ancestorLayer, int &x, int &y) const
656 {
657 if (ancestorLayer == this) {
658 return;
659 }
660
661 if (m_object->style()->position() == PFIXED) {
662 // Add in the offset of the view. We can obtain this by calling
663 // absolutePosition() on the RenderCanvas.
664 int xOff, yOff;
665 m_object->absolutePosition(xOff, yOff, true);
666 x += xOff;
667 y += yOff;
668 return;
669 }
670
671 RenderLayer *parentLayer;
672 if (m_object->style()->position() == PABSOLUTE) {
673 parentLayer = enclosingPositionedAncestor();
674 } else {
675 parentLayer = parent();
676 }
677
678 if (!parentLayer) {
679 return;
680 }
681
682 parentLayer->convertToLayerCoords(ancestorLayer, x, y);
683
684 x += xPos();
685 y += yPos();
686 }
687
scrollOffset(int & x,int & y)688 void RenderLayer::scrollOffset(int &x, int &y)
689 {
690 x += scrollXOffset();
691 y += scrollYOffset();
692 }
693
subtractScrollOffset(int & x,int & y)694 void RenderLayer::subtractScrollOffset(int &x, int &y)
695 {
696 x -= scrollXOffset();
697 y -= scrollYOffset();
698 }
699
checkInlineRelOffset(const RenderObject * o,int & x,int & y)700 void RenderLayer::checkInlineRelOffset(const RenderObject *o, int &x, int &y)
701 {
702 if (o->style()->position() != PABSOLUTE || !renderer()->isRelPositioned() || !renderer()->isInlineFlow()) {
703 return;
704 }
705
706 // Our renderer is an enclosing relpositioned inline, we need to add in the offset of the first line
707 // box from the rest of the content, but only in the cases where we know our descendant is positioned
708 // relative to the inline itself.
709 assert(o->container() == m_object);
710
711 RenderFlow *flow = static_cast<RenderFlow *>(m_object);
712 int sx = 0, sy = 0;
713 if (flow->firstLineBox()) {
714 if (flow->style()->direction() == LTR) {
715 sx = flow->firstLineBox()->xPos();
716 } else {
717 sx = flow->lastLineBox()->xPos();
718 }
719 sy = flow->firstLineBox()->yPos();
720 } else {
721 sx = flow->staticX(); // ###
722 sy = flow->staticY();
723 }
724 bool isInlineType = o->style()->isOriginalDisplayInlineType();
725
726 if (!o->hasStaticX()) {
727 x += sx;
728 }
729
730 // Despite the positioned child being a block display type inside an inline, we still keep
731 // its x locked to our left. Arguably the correct behavior would be to go flush left to
732 // the block that contains us, but that isn't what other browsers do.
733 if (o->hasStaticX() && !isInlineType)
734 // Avoid adding in the left border/padding of the containing block twice. Subtract it out.
735 {
736 x += sx - (o->containingBlock()->borderLeft() + o->containingBlock()->paddingLeft());
737 }
738
739 if (!o->hasStaticY()) {
740 y += sy;
741 }
742 }
743
scrollToOffset(int x,int y,bool updateScrollbars,bool repaint,bool dispatchEvent)744 void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint, bool dispatchEvent)
745 {
746 assert(!renderer()->canvas()->isPerformingLayout() || !dispatchEvent);
747 if (renderer()->style()->overflowX() != OMARQUEE || !renderer()->hasOverflowClip()) {
748 if (x < 0) {
749 x = 0;
750 }
751 if (y < 0) {
752 y = 0;
753 }
754
755 // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
756 // to be (for overflow:hidden blocks).
757 // ### merge the scrollWidth()/scrollHeight() methods
758 int maxX = m_scrollWidth - m_object->clientWidth();
759 int maxY = m_scrollHeight - m_object->clientHeight();
760
761 if (x > maxX) {
762 x = maxX;
763 }
764 if (y > maxY) {
765 y = maxY;
766 }
767 }
768
769 if ((m_scrollX == x - m_scrollXOrigin) && m_scrollY == y) {
770 return; // nothing to do
771 }
772
773 // FIXME: Eventually, we will want to perform a blit. For now never
774 // blit, since the check for blitting is going to be very
775 // complicated (since it will involve testing whether our layer
776 // is either occluded by another layer or clipped by an enclosing
777 // layer or contains fixed backgrounds, etc.).
778 m_scrollX = x - m_scrollXOrigin;
779 m_scrollY = y;
780
781 // Update the positions of our child layers.
782 RenderLayer *rootLayer = root();
783 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
784 child->updateLayerPositions(rootLayer);
785 }
786
787 // Just schedule a full repaint of our object.
788 if (repaint) {
789 m_object->repaint(RealtimePriority);
790 }
791
792 if (updateScrollbars) {
793 if (m_hBar) {
794 m_hBar->setValue(scrollXOffset());
795 }
796 if (m_vBar) {
797 m_vBar->setValue(m_scrollY);
798 }
799 }
800
801 if (!dispatchEvent) {
802 return;
803 }
804
805 // Fire the scroll DOM event. Do this the very last thing, since the handler may kill us.
806 m_object->element()->dispatchHTMLEvent(EventImpl::SCROLL_EVENT, false, false);
807 }
808
updateScrollPositionFromScrollbars()809 void RenderLayer::updateScrollPositionFromScrollbars()
810 {
811 bool needUpdate = false;
812 int newX = m_scrollX;
813 int newY = m_scrollY;
814
815 if (m_hBar) {
816 bool rtl = (m_hBar->layoutDirection() == Qt::RightToLeft);
817 newX = rtl ? m_hBar->maximum() - m_hBar->value() : m_hBar->value();
818 if (newX != m_scrollX) {
819 needUpdate = true;
820 }
821 }
822
823 if (m_vBar) {
824 newY = m_vBar->value();
825 if (newY != m_scrollY) {
826 needUpdate = true;
827 }
828 }
829
830 if (needUpdate) {
831 scrollToOffset(newX, newY, false);
832 }
833 }
834
835 void
showScrollbar(Qt::Orientation o,bool show)836 RenderLayer::showScrollbar(Qt::Orientation o, bool show)
837 {
838 ScrollBarWidget *sb = (o == Qt::Horizontal) ? m_hBar : m_vBar;
839
840 if (show && !sb) {
841 KHTMLView *view = m_object->document()->view();
842 sb = new ScrollBarWidget(o, view->widget());
843 sb->move(0, -50000);
844 sb->setAttribute(Qt::WA_NoSystemBackground);
845 sb->show();
846 if (!m_scrollMediator) {
847 m_scrollMediator = new RenderScrollMediator(this);
848 }
849 m_scrollMediator->connect(sb, SIGNAL(valueChanged(int)), SLOT(slotValueChanged()));
850 } else if (!show && sb) {
851 delete sb;
852 sb = nullptr;
853 }
854
855 if (o == Qt::Horizontal) {
856 m_hBar = sb;
857 } else {
858 m_vBar = sb;
859 }
860 }
861
hasReversedScrollbar() const862 bool RenderLayer::hasReversedScrollbar() const
863 {
864 if (!m_vBar) {
865 return false;
866 }
867 return (m_vBar->layoutDirection() == Qt::RightToLeft);
868 }
869
verticalScrollbarWidth()870 int RenderLayer::verticalScrollbarWidth()
871 {
872 if (!m_vBar) {
873 return 0;
874 }
875
876 #ifdef APPLE_CHANGES
877 return m_vBar->width();
878 #else
879 return m_vBar->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
880 #endif
881
882 }
883
horizontalScrollbarHeight()884 int RenderLayer::horizontalScrollbarHeight()
885 {
886 if (!m_hBar) {
887 return 0;
888 }
889
890 #ifdef APPLE_CHANGES
891 return m_hBar->height();
892 #else
893 return m_hBar->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
894 #endif
895
896 }
897
positionScrollbars(const QRect & absBounds)898 void RenderLayer::positionScrollbars(const QRect &absBounds)
899 {
900 #ifdef APPLE_CHANGES
901 if (m_vBar) {
902 view->addChild(m_vBar, absBounds.x() + absBounds.width() - m_object->borderRight() - m_vBar->width(),
903 absBounds.y() + m_object->borderTop());
904 m_vBar->resize(m_vBar->width(), absBounds.height() -
905 (m_object->borderTop() + m_object->borderBottom()) -
906 (m_hBar ? m_hBar->height() - 1 : 0));
907 }
908
909 if (m_hBar) {
910 view->addChild(m_hBar, absBounds.x() + m_object->borderLeft(),
911 absBounds.y() + absBounds.height() - m_object->borderBottom() - m_hBar->height());
912 m_hBar->resize(absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) -
913 (m_vBar ? m_vBar->width() - 1 : 0), m_hBar->height());
914 }
915 #else
916 int tx = absBounds.x();
917 int ty = absBounds.y();
918 int bl = m_object->borderLeft();
919 int bt = m_object->borderTop();
920 int w = width() - bl - m_object->borderRight();
921 int h = height() - bt - m_object->borderBottom();
922
923 if (w <= 0 || h <= 0 || (!m_vBar && !m_hBar)) {
924 return;
925 }
926
927 tx += bl;
928 ty += bt;
929
930 ScrollBarWidget *b = m_hBar;
931 if (!m_hBar) {
932 b = m_vBar;
933 }
934 int sw = b->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
935 bool rtl = b->layoutDirection() == Qt::RightToLeft;
936
937 if (m_vBar) {
938 QRect vBarRect = QRect(tx + (rtl ? 0 : w - sw), ty, sw, h - (m_hBar ? sw : 0));
939 m_vBar->resize(vBarRect.width(), vBarRect.height());
940 m_vBar->m_kwp->setPos(QPoint(vBarRect.x(), vBarRect.y()));
941 }
942
943 if (m_hBar) {
944 QRect hBarRect = QRect(tx + (rtl && m_vBar ? sw : 0), ty + h - sw, w - (!rtl && m_vBar ? sw : 0), sw);
945 m_hBar->resize(hBarRect.width(), hBarRect.height());
946 m_hBar->m_kwp->setPos(QPoint(hBarRect.x(), hBarRect.y()));
947 }
948 #endif
949 }
950
951 #define LINE_STEP 10
952 #define PAGE_KEEP 40
953
checkScrollbarsAfterLayout()954 void RenderLayer::checkScrollbarsAfterLayout()
955 {
956 int rightPos = m_object->rightmostPosition(true);
957 int bottomPos = m_object->lowestPosition(true);
958
959 /* TODO
960 m_scrollLeft = m_object->leftmostPosition(true);
961 m_scrollTop = m_object->highestPosition(true);
962 */
963
964 int clientWidth = m_object->clientWidth();
965 int clientHeight = m_object->clientHeight();
966 m_scrollWidth = clientWidth;
967 m_scrollHeight = clientHeight;
968
969 if (rightPos - m_object->borderLeft() > m_scrollWidth) {
970 m_scrollWidth = rightPos - m_object->borderLeft();
971 }
972 if (bottomPos - m_object->borderTop() > m_scrollHeight) {
973 m_scrollHeight = bottomPos - m_object->borderTop();
974 }
975
976 m_scrollXOrigin = 0; // ### (m_object->style()->direction() == RTL) ? m_scrollWidth - clientWidth : 0;
977
978 bool needHorizontalBar = rightPos > width();
979 bool needVerticalBar = bottomPos > height();
980
981 bool haveHorizontalBar = m_hBar && m_hBar->isEnabled();
982 bool haveVerticalBar = m_vBar && m_vBar->isEnabled();
983
984 bool hasOvf = m_object->hasOverflowClip();
985
986 // overflow:scroll should just enable/disable.
987 if (m_hBar && hasOvf && m_object->style()->overflowX() == OSCROLL) {
988 m_hBar->setEnabled(needHorizontalBar);
989 }
990 if (m_vBar && hasOvf && m_object->style()->overflowY() == OSCROLL) {
991 m_vBar->setEnabled(needVerticalBar);
992 }
993
994 // Sometimes we originally had a scrolling overflow, but it got changed to
995 // hidden/visible.
996 bool deadScrollX = m_hBar && !m_object->scrollsOverflowX();
997 bool deadScrollY = m_vBar && !m_object->scrollsOverflowY();
998
999 // overflow:auto may need to lay out again if scrollbars got added/removed.
1000 // Also remove now useless scrollbars for non-scrollable overflows
1001 bool scrollbarsChanged = (hasOvf && m_object->style()->overflowX() == OAUTO && haveHorizontalBar != needHorizontalBar)
1002 || (hasOvf && m_object->style()->overflowY() == OAUTO && haveVerticalBar != needVerticalBar)
1003 || deadScrollX || deadScrollY;
1004 if (scrollbarsChanged && !m_inScrollbarRelayout) {
1005 if (m_object->style()->overflowX() == OAUTO) {
1006 showScrollbar(Qt::Horizontal, needHorizontalBar);
1007 if (m_hBar) {
1008 m_hBar->setEnabled(true);
1009 } else {
1010 resetXOffset();
1011 }
1012 }
1013 if (m_object->style()->overflowY() == OAUTO) {
1014 showScrollbar(Qt::Vertical, needVerticalBar);
1015 if (m_vBar) {
1016 m_vBar->setEnabled(true);
1017 } else {
1018 resetYOffset();
1019 }
1020 }
1021
1022 if (deadScrollX) {
1023 showScrollbar(Qt::Horizontal, false);
1024 resetXOffset();
1025 }
1026
1027 if (deadScrollY) {
1028 showScrollbar(Qt::Vertical, false);
1029 resetYOffset();
1030 }
1031
1032 m_object->setNeedsLayout(true);
1033 m_inScrollbarRelayout = true;
1034 if (m_object->isRenderBlock()) {
1035 static_cast<RenderBlock *>(m_object)->layoutBlock(true);
1036 } else {
1037 m_object->layout();
1038 }
1039 m_inScrollbarRelayout = false;
1040 return;
1041 }
1042
1043 m_inScrollbarRelayout = false;
1044
1045 // Set up the range (and page step/line step).
1046 if (m_hBar) {
1047 int pageStep = (clientWidth - PAGE_KEEP);
1048 if (pageStep < 0) {
1049 pageStep = clientWidth;
1050 }
1051 m_hBar->setSingleStep(LINE_STEP);
1052 m_hBar->setPageStep(pageStep);
1053 m_hBar->setRange(0, needHorizontalBar ? m_scrollWidth - clientWidth : 0);
1054 if (hasReversedScrollbar()) {
1055 m_hBar->setValue(m_hBar->maximum() - m_scrollX);
1056 }
1057 }
1058 if (m_vBar) {
1059 int pageStep = (clientHeight - PAGE_KEEP);
1060 if (pageStep < 0) {
1061 pageStep = clientHeight;
1062 }
1063 m_vBar->setSingleStep(LINE_STEP);
1064 m_vBar->setPageStep(pageStep);
1065 m_vBar->setRange(0, needVerticalBar ? m_scrollHeight - clientHeight : 0);
1066 }
1067 }
1068
paintScrollbars(RenderObject::PaintInfo & pI)1069 void RenderLayer::paintScrollbars(RenderObject::PaintInfo &pI)
1070 {
1071 if (!m_object->element()) {
1072 return;
1073 }
1074
1075 if (m_hBar) {
1076 if (!m_buffer[0] || m_buffer[0]->size() != m_hBar->size()) {
1077 delete m_buffer[0];
1078 m_buffer[0] = new QPixmap(m_hBar->size());
1079 }
1080 QPoint p = m_hBar->m_kwp->absolutePos();
1081 RenderWidget::paintWidget(pI, m_hBar, p.x(), p.y(), m_buffer);
1082 }
1083 if (m_vBar) {
1084 if (!m_buffer[1] || m_buffer[1]->size() != m_vBar->size()) {
1085 delete m_buffer[1];
1086 m_buffer[1] = new QPixmap(m_vBar->size());
1087 }
1088 QPixmap *tmp[1];
1089 tmp[0] = m_buffer[1];
1090 QPoint p = m_vBar->m_kwp->absolutePos();
1091 RenderWidget::paintWidget(pI, m_vBar, p.x(), p.y(), tmp);
1092 }
1093 }
1094
paint(QPainter * p,const QRect & damageRect,bool selectionOnly)1095 void RenderLayer::paint(QPainter *p, const QRect &damageRect, bool selectionOnly)
1096 {
1097 paintLayer(this, p, damageRect, selectionOnly);
1098 }
1099
setClip(QPainter * p,const QRect & paintDirtyRect,const QRect & clipRect,bool)1100 void RenderLayer::setClip(QPainter *p, const QRect &paintDirtyRect, const QRect &clipRect, bool /*setup*/)
1101 {
1102 if (paintDirtyRect == clipRect) {
1103 return;
1104 }
1105 KHTMLView *v = m_object->canvas()->view();
1106 QRegion r = clipRect;
1107 if (p->hasClipping()) {
1108 if (!v->clipHolder()) {
1109 v->setClipHolder(new QStack<QRegion>);
1110 }
1111 v->clipHolder()->push(p->clipRegion());
1112 r &= v->clipHolder()->top();
1113 }
1114 p->setClipRegion(r);
1115 }
1116
restoreClip(QPainter * p,const QRect & paintDirtyRect,const QRect & clipRect,bool)1117 void RenderLayer::restoreClip(QPainter *p, const QRect &paintDirtyRect, const QRect &clipRect, bool /*cleanup*/)
1118 {
1119 if (paintDirtyRect == clipRect) {
1120 return;
1121 }
1122 KHTMLView *v = m_object->document()->view();
1123 if (v->clipHolder() && !v->clipHolder()->isEmpty()) {
1124 p->setClipRegion(v->clipHolder()->pop());
1125 } else {
1126 p->setClipRegion(QRegion(), Qt::NoClip);
1127 }
1128 }
1129
paintLayer(RenderLayer * rootLayer,QPainter * p,const QRect & paintDirtyRect,bool selectionOnly)1130 void RenderLayer::paintLayer(RenderLayer *rootLayer, QPainter *p,
1131 const QRect &paintDirtyRect, bool selectionOnly)
1132 {
1133 assert(rootLayer != this || !m_object->canvas()->view()->clipHolder());
1134
1135 if (!m_object->style()->opacity()) {
1136 return;
1137 }
1138
1139 // Calculate the clip rects we should use.
1140 QRect layerBounds, damageRect, clipRectToApply;
1141 calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
1142 int x = layerBounds.x();
1143 int y = layerBounds.y();
1144
1145 // Ensure our lists are up-to-date.
1146 updateZOrderLists();
1147 updateOverflowList();
1148
1149 // Set our transparency if we need to.
1150 khtml::BufferedPainter *bPainter = nullptr;
1151 if (isTransparent()) {
1152 //### cache paintedRegion
1153 QRegion rr = paintedRegion(rootLayer) & damageRect;
1154 if (p->hasClipping()) {
1155 rr &= p->clipRegion();
1156 }
1157 bPainter = khtml::BufferedPainter::start(p, rr);
1158 }
1159 // We want to paint our layer, but only if we intersect the damage rect.
1160 bool shouldPaint = intersectsDamageRect(layerBounds, damageRect) && m_hasVisibleContent;
1161 if (shouldPaint && !selectionOnly) {
1162 // Paint our background first, before painting any child layers.
1163 if (!damageRect.isEmpty()) {
1164 // Establish the clip used to paint our background.
1165 setClip(p, paintDirtyRect, damageRect);
1166
1167 // Paint the background.
1168 RenderObject::PaintInfo paintInfo(p, damageRect, PaintActionElementBackground);
1169 renderer()->paint(paintInfo,
1170 x - renderer()->xPos(), y - renderer()->yPos() + renderer()->borderTopExtra());
1171
1172 // Position our scrollbars.
1173 positionScrollbars(layerBounds);
1174
1175 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with
1176 // z-index. We paint after we painted the background/border, so that the scrollbars will
1177 // sit above the background/border.
1178 paintScrollbars(paintInfo);
1179
1180 // Restore the clip.
1181 restoreClip(p, paintDirtyRect, damageRect);
1182 }
1183 }
1184
1185 // Now walk the sorted list of children with negative z-indices.
1186 if (m_negZOrderList) {
1187 for (int i = 0; i < m_negZOrderList->count(); i++) {
1188 RenderLayer *child = m_negZOrderList->at(i);
1189 child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
1190 }
1191 }
1192
1193 // Now establish the appropriate clip and paint our child RenderObjects.
1194 if (shouldPaint && !clipRectToApply.isEmpty()) {
1195 // Set up the clip used when painting our children.
1196 setClip(p, paintDirtyRect, clipRectToApply);
1197
1198 RenderObject::PaintInfo paintInfo(p, clipRectToApply, PaintActionSelection);
1199
1200 int tx = x - renderer()->xPos();
1201 int ty = y - renderer()->yPos() + renderer()->borderTopExtra();
1202
1203 if (selectionOnly) {
1204 renderer()->paint(paintInfo, tx, ty);
1205 } else {
1206 paintInfo.phase = PaintActionChildBackgrounds;
1207 renderer()->paint(paintInfo, tx, ty);
1208 paintInfo.phase = PaintActionFloat;
1209 renderer()->paint(paintInfo, tx, ty);
1210 paintInfo.phase = PaintActionForeground;
1211 renderer()->paint(paintInfo, tx, ty);
1212 RenderCanvas *rc = static_cast<RenderCanvas *>(renderer()->document()->renderer());
1213 if (rc->maximalOutlineSize()) {
1214 paintInfo.phase = PaintActionOutline;
1215 renderer()->paint(paintInfo, tx, ty);
1216 }
1217 if (renderer()->canvas()->hasSelection()) {
1218 paintInfo.phase = PaintActionSelection;
1219 renderer()->paint(paintInfo, tx, ty);
1220 }
1221 }
1222
1223 // Now restore our clip.
1224 restoreClip(p, paintDirtyRect, clipRectToApply);
1225 }
1226
1227 // Paint any child layers that have overflow.
1228 if (m_overflowList)
1229 foreach (RenderLayer *layer, *m_overflowList) {
1230 layer->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
1231 }
1232
1233 // Now walk the sorted list of children with positive z-indices.
1234 if (m_posZOrderList) {
1235 for (int i = 0; i < m_posZOrderList->count(); i++) {
1236 RenderLayer *child = m_posZOrderList->at(i);
1237 child->paintLayer(rootLayer, p, paintDirtyRect, selectionOnly);
1238 }
1239 }
1240
1241 #ifdef BOX_DEBUG
1242 {
1243 int ax = 0;
1244 int ay = 0;
1245 renderer()->absolutePosition(ax, ay);
1246 p->setPen(QPen(QColor("yellow"), 1, Qt::DotLine));
1247 p->setBrush(Qt::NoBrush);
1248 p->drawRect(ax, ay, width(), height());
1249 }
1250 #endif
1251
1252 // End our transparency layer
1253 if (bPainter) {
1254 khtml::BufferedPainter::end(p, bPainter, m_object->style()->opacity());
1255 }
1256
1257 if (rootLayer == this && m_object->canvas()->view()->clipHolder()) {
1258 KHTMLView *const v = m_object->canvas()->view();
1259 assert(v->clipHolder()->isEmpty());
1260 delete v->clipHolder();
1261 v->setClipHolder(nullptr);
1262 }
1263 }
1264
nodeAtPoint(RenderObject::NodeInfo & info,int x,int y)1265 bool RenderLayer::nodeAtPoint(RenderObject::NodeInfo &info, int x, int y)
1266 {
1267 // Clear our our scrollbar variable
1268 RenderLayer::gScrollBar = nullptr;
1269
1270 int stx = m_x;
1271 int sty = m_y;
1272
1273 if (renderer()->isCanvas()) {
1274 static_cast<RenderCanvas *>(renderer())->view()->revertTransforms(stx, sty);
1275 }
1276
1277 QRect damageRect(stx, sty, width(), height());
1278 RenderLayer *insideLayer = nodeAtPointForLayer(this, info, x, y, damageRect);
1279
1280 // Now determine if the result is inside an anchor.
1281 DOM::NodeImpl *node = info.innerNode();
1282 while (node) {
1283 if (node->hasAnchor() && !info.URLElement()) {
1284 info.setURLElement(node);
1285 }
1286 node = node->parentNode();
1287 }
1288
1289 // Next set up the correct :hover/:active state along the new chain.
1290 updateHoverActiveState(info);
1291
1292 // Now return whether we were inside this layer (this will always be true for the root
1293 // layer).
1294 return insideLayer;
1295 }
1296
nodeAtPointForLayer(RenderLayer * rootLayer,RenderObject::NodeInfo & info,int xMousePos,int yMousePos,const QRect & hitTestRect)1297 RenderLayer *RenderLayer::nodeAtPointForLayer(RenderLayer *rootLayer, RenderObject::NodeInfo &info,
1298 int xMousePos, int yMousePos, const QRect &hitTestRect)
1299 {
1300 // Calculate the clip rects we should use.
1301 QRect layerBounds, bgRect, fgRect;
1302 calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect);
1303
1304 // Ensure our lists are up-to-date.
1305 updateZOrderLists();
1306 updateOverflowList();
1307
1308 // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer,
1309 // we are done and can return it.
1310 RenderLayer *insideLayer = nullptr;
1311
1312 // Begin by walking our list of positive layers from highest z-index down to the lowest
1313 // z-index.
1314 if (m_posZOrderList) {
1315 uint count = m_posZOrderList->count();
1316 for (int i = count - 1; i >= 0; i--) {
1317 RenderLayer *child = m_posZOrderList->at(i);
1318 insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1319 if (insideLayer) {
1320 return insideLayer;
1321 }
1322 }
1323 }
1324
1325 // Now check our overflow objects.
1326 if (m_overflowList) {
1327 QVector<RenderLayer *>::iterator it = m_overflowList->end();
1328 while (it != m_overflowList->begin()) {
1329 --it;
1330 insideLayer = (*it)->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1331 if (insideLayer) {
1332 return insideLayer;
1333 }
1334 }
1335 }
1336
1337 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer.
1338 if (containsPoint(xMousePos, yMousePos, fgRect) &&
1339 renderer()->nodeAtPoint(info, xMousePos, yMousePos,
1340 layerBounds.x() - renderer()->xPos(),
1341 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
1342 HitTestChildrenOnly)) {
1343 if (info.innerNode() != m_object->element()) {
1344 return this;
1345 }
1346 }
1347
1348 // Now check our negative z-index children.
1349 if (m_negZOrderList) {
1350 uint count = m_negZOrderList->count();
1351 for (int i = count - 1; i >= 0; i--) {
1352 RenderLayer *child = m_negZOrderList->at(i);
1353 insideLayer = child->nodeAtPointForLayer(rootLayer, info, xMousePos, yMousePos, hitTestRect);
1354 if (insideLayer) {
1355 return insideLayer;
1356 }
1357 }
1358 }
1359
1360 // Next we want to see if the mouse pos is inside this layer but not any of its children.
1361 if (containsPoint(xMousePos, yMousePos, bgRect) &&
1362 renderer()->nodeAtPoint(info, xMousePos, yMousePos,
1363 layerBounds.x() - renderer()->xPos(),
1364 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(),
1365 HitTestSelfOnly)) {
1366 return this;
1367 }
1368
1369 // No luck.
1370 return nullptr;
1371 }
1372
calculateClipRects(const RenderLayer * rootLayer,QRect & overflowClipRect,QRect & posClipRect,QRect & fixedClipRect)1373 void RenderLayer::calculateClipRects(const RenderLayer *rootLayer, QRect &overflowClipRect,
1374 QRect &posClipRect, QRect &fixedClipRect)
1375 {
1376 if (parent()) {
1377 parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
1378 }
1379
1380 switch (m_object->style()->position()) {
1381 // A fixed object is essentially the root of its containing block hierarchy, so when
1382 // we encounter such an object, we reset our clip rects to the fixedClipRect.
1383 case PFIXED:
1384 posClipRect = fixedClipRect;
1385 overflowClipRect = fixedClipRect;
1386 break;
1387 case PABSOLUTE:
1388 overflowClipRect = posClipRect;
1389 break;
1390 case PRELATIVE:
1391 posClipRect = overflowClipRect;
1392 break;
1393 default:
1394 break;
1395 }
1396
1397 // Update the clip rects that will be passed to child layers.
1398 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1399 // This layer establishes a clip of some kind.
1400 int x = 0;
1401 int y = 0;
1402 convertToLayerCoords(rootLayer, x, y);
1403
1404 if (m_object->hasOverflowClip()) {
1405 QRect newOverflowClip = m_object->overflowClipRect(x, y);
1406 overflowClipRect = newOverflowClip.intersected(overflowClipRect);
1407 if (m_object->isPositioned() || m_object->isRelPositioned()) {
1408 posClipRect = newOverflowClip.intersected(posClipRect);
1409 }
1410 }
1411 if (m_object->hasClip()) {
1412 QRect newPosClip = m_object->clipRect(x, y);
1413 posClipRect = posClipRect.intersected(newPosClip);
1414 overflowClipRect = overflowClipRect.intersected(newPosClip);
1415 fixedClipRect = fixedClipRect.intersected(newPosClip);
1416 }
1417 }
1418 }
1419
calculateRects(const RenderLayer * rootLayer,const QRect & paintDirtyRect,QRect & layerBounds,QRect & backgroundRect,QRect & foregroundRect)1420 void RenderLayer::calculateRects(const RenderLayer *rootLayer, const QRect &paintDirtyRect, QRect &layerBounds,
1421 QRect &backgroundRect, QRect &foregroundRect)
1422 {
1423 QRect overflowClipRect = paintDirtyRect;
1424 QRect posClipRect = paintDirtyRect;
1425 QRect fixedClipRect = paintDirtyRect;
1426 if (parent()) {
1427 parent()->calculateClipRects(rootLayer, overflowClipRect, posClipRect, fixedClipRect);
1428 }
1429
1430 int x = 0;
1431 int y = 0;
1432 convertToLayerCoords(rootLayer, x, y);
1433 layerBounds = QRect(x, y, width(), height());
1434
1435 backgroundRect = m_object->style()->position() == PFIXED ? fixedClipRect :
1436 (m_object->isPositioned() ? posClipRect : overflowClipRect);
1437 foregroundRect = backgroundRect;
1438
1439 // Update the clip rects that will be passed to child layers.
1440 if (m_object->hasOverflowClip() || m_object->hasClip()) {
1441 // This layer establishes a clip of some kind.
1442 if (m_object->hasOverflowClip()) {
1443 foregroundRect = foregroundRect.intersected(m_object->overflowClipRect(x, y));
1444 }
1445
1446 if (m_object->hasClip()) {
1447 // Clip applies to *us* as well, so go ahead and update the damageRect.
1448 QRect newPosClip = m_object->clipRect(x, y);
1449 backgroundRect = backgroundRect.intersected(newPosClip);
1450 foregroundRect = foregroundRect.intersected(newPosClip);
1451 }
1452
1453 // If we establish a clip at all, then go ahead and make sure our background
1454 // rect is intersected with our layer's bounds.
1455 backgroundRect = backgroundRect.intersected(layerBounds);
1456 }
1457 }
1458
intersectsDamageRect(const QRect & layerBounds,const QRect & damageRect) const1459 bool RenderLayer::intersectsDamageRect(const QRect &layerBounds, const QRect &damageRect) const
1460 {
1461 return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isBody() ||
1462 (renderer()->hasOverhangingFloats() && !renderer()->hasOverflowClip()) ||
1463 (renderer()->isInline() && !renderer()->isReplaced()) ||
1464 layerBounds.intersects(damageRect));
1465 }
1466
containsPoint(int x,int y,const QRect & damageRect) const1467 bool RenderLayer::containsPoint(int x, int y, const QRect &damageRect) const
1468 {
1469 return (renderer()->isCanvas() || renderer()->isRoot() || renderer()->isInlineFlow() ||
1470 damageRect.contains(x, y));
1471 }
1472
1473 // This code has been written to anticipate the addition of CSS3-::outside and ::inside generated
1474 // content (and perhaps XBL). That's why it uses the render tree and not the DOM tree.
hoverAncestor(RenderObject * obj)1475 static RenderObject *hoverAncestor(RenderObject *obj)
1476 {
1477 return (!obj->isInline() && obj->continuation()) ? obj->continuation() : obj->parent();
1478 }
1479
commonAncestor(RenderObject * obj1,RenderObject * obj2)1480 static RenderObject *commonAncestor(RenderObject *obj1, RenderObject *obj2)
1481 {
1482 if (!obj1 || !obj2) {
1483 return nullptr;
1484 }
1485
1486 for (RenderObject *currObj1 = obj1; currObj1; currObj1 = hoverAncestor(currObj1))
1487 for (RenderObject *currObj2 = obj2; currObj2; currObj2 = hoverAncestor(currObj2))
1488 if (currObj1 == currObj2) {
1489 return currObj1;
1490 }
1491
1492 return nullptr;
1493 }
1494
updateHoverActiveState(RenderObject::NodeInfo & info)1495 void RenderLayer::updateHoverActiveState(RenderObject::NodeInfo &info)
1496 {
1497 // We don't update :hover/:active state when the info is marked as readonly.
1498 if (info.readonly()) {
1499 return;
1500 }
1501
1502 DOM::NodeImpl *e = m_object->element();
1503 DOM::DocumentImpl *doc = e ? e->document() : nullptr;
1504 if (!doc) {
1505 return;
1506 }
1507
1508 // Check to see if the hovered node has changed. If not, then we don't need to
1509 // do anything.
1510 DOM::NodeImpl *oldHoverNode = doc->hoverNode();
1511 DOM::NodeImpl *newHoverNode = info.innerNode();
1512
1513 if (oldHoverNode == newHoverNode && (!oldHoverNode || oldHoverNode->active() == info.active())) {
1514 return;
1515 }
1516
1517 // Update our current hover node.
1518 doc->setHoverNode(newHoverNode);
1519 if (info.active()) {
1520 doc->setActiveNode(newHoverNode);
1521 } else {
1522 doc->setActiveNode(nullptr);
1523 }
1524
1525 // We have two different objects. Fetch their renderers.
1526 RenderObject *oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : nullptr;
1527 RenderObject *newHoverObj = newHoverNode ? newHoverNode->renderer() : nullptr;
1528
1529 // Locate the common ancestor render object for the two renderers.
1530 RenderObject *ancestor = commonAncestor(oldHoverObj, newHoverObj);
1531
1532 // The old hover path only needs to be cleared up to (and not including) the common ancestor;
1533 for (RenderObject *curr = oldHoverObj; curr && curr != ancestor; curr = hoverAncestor(curr)) {
1534 curr->setMouseInside(false);
1535 if (curr->element()) {
1536 curr->element()->setActive(false);
1537 curr->element()->setHovered(false);
1538 }
1539 }
1540
1541 // Now set the hover state for our new object up to the root.
1542 for (RenderObject *curr = newHoverObj; curr; curr = hoverAncestor(curr)) {
1543 curr->setMouseInside(true);
1544 if (curr->element()) {
1545 curr->element()->setActive(info.active());
1546 curr->element()->setHovered(true);
1547 }
1548 }
1549 }
1550
1551 // Sort the buffer from lowest z-index to highest. The common scenario will have
1552 // most z-indices equal, so we optimize for that case (i.e., the list will be mostly
1553 // sorted already).
sortByZOrder(QVector<RenderLayer * > * buffer,QVector<RenderLayer * > * mergeBuffer,uint start,uint end)1554 static void sortByZOrder(QVector<RenderLayer *> *buffer,
1555 QVector<RenderLayer *> *mergeBuffer,
1556 uint start, uint end)
1557 {
1558 if (start >= end) {
1559 return; // Sanity check.
1560 }
1561
1562 if (end - start <= 6) {
1563 // Apply a bubble sort for smaller lists.
1564 for (uint i = end - 1; i > start; i--) {
1565 bool sorted = true;
1566 for (uint j = start; j < i; j++) {
1567 RenderLayer *elt = buffer->at(j);
1568 RenderLayer *elt2 = buffer->at(j + 1);
1569 if (elt->zIndex() > elt2->zIndex()) {
1570 sorted = false;
1571 buffer->replace(j, elt2);
1572 buffer->replace(j + 1, elt);
1573 }
1574 }
1575 if (sorted) {
1576 return;
1577 }
1578 }
1579 } else {
1580 // Peform a merge sort for larger lists.
1581 uint mid = (start + end) / 2;
1582 sortByZOrder(buffer, mergeBuffer, start, mid);
1583 sortByZOrder(buffer, mergeBuffer, mid, end);
1584
1585 RenderLayer *elt = buffer->at(mid - 1);
1586 RenderLayer *elt2 = buffer->at(mid);
1587
1588 // Handle the fast common case (of equal z-indices). The list may already
1589 // be completely sorted.
1590 if (elt->zIndex() <= elt2->zIndex()) {
1591 return;
1592 }
1593
1594 // We have to merge sort.
1595 uint i1 = start;
1596 uint i2 = mid;
1597
1598 elt = buffer->at(i1);
1599 elt2 = buffer->at(i2);
1600
1601 while (i1 < mid || i2 < end) {
1602 if (i1 < mid && (i2 == end || elt->zIndex() <= elt2->zIndex())) {
1603 mergeBuffer->append(elt);
1604 i1++;
1605 if (i1 < mid) {
1606 elt = buffer->at(i1);
1607 }
1608 } else {
1609 mergeBuffer->append(elt2);
1610 i2++;
1611 if (i2 < end) {
1612 elt2 = buffer->at(i2);
1613 }
1614 }
1615 }
1616
1617 for (uint i = start; i < end; i++) {
1618 buffer->replace(i, mergeBuffer->at(i - start));
1619 }
1620
1621 mergeBuffer->clear();
1622 }
1623 }
1624
dirtyZOrderLists()1625 void RenderLayer::dirtyZOrderLists()
1626 {
1627 if (m_posZOrderList) {
1628 m_posZOrderList->clear();
1629 }
1630 if (m_negZOrderList) {
1631 m_negZOrderList->clear();
1632 }
1633 m_zOrderListsDirty = true;
1634 }
1635
dirtyOverflowList()1636 void RenderLayer::dirtyOverflowList()
1637 {
1638 if (m_overflowList) {
1639 m_overflowList->clear();
1640 }
1641 m_overflowListDirty = true;
1642 }
1643
updateZOrderLists()1644 void RenderLayer::updateZOrderLists()
1645 {
1646 if (!isStackingContext() || !m_zOrderListsDirty) {
1647 return;
1648 }
1649
1650 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
1651 child->collectLayers(m_posZOrderList, m_negZOrderList);
1652 }
1653
1654 // Sort the two lists.
1655 if (m_posZOrderList) {
1656 QVector<RenderLayer *> mergeBuffer;
1657 sortByZOrder(m_posZOrderList, &mergeBuffer, 0, m_posZOrderList->count());
1658 }
1659 if (m_negZOrderList) {
1660 QVector<RenderLayer *> mergeBuffer;
1661 sortByZOrder(m_negZOrderList, &mergeBuffer, 0, m_negZOrderList->count());
1662 }
1663
1664 m_zOrderListsDirty = false;
1665 }
1666
updateOverflowList()1667 void RenderLayer::updateOverflowList()
1668 {
1669 if (!m_overflowListDirty) {
1670 return;
1671 }
1672
1673 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
1674 if (child->isOverflowOnly()) {
1675 if (!m_overflowList) {
1676 m_overflowList = new QVector<RenderLayer *>;
1677 }
1678 m_overflowList->append(child);
1679 }
1680 }
1681
1682 m_overflowListDirty = false;
1683 }
1684
collectLayers(QVector<RenderLayer * > * & posBuffer,QVector<RenderLayer * > * & negBuffer)1685 void RenderLayer::collectLayers(QVector<RenderLayer *> *&posBuffer, QVector<RenderLayer *> *&negBuffer)
1686 {
1687 updateVisibilityStatus();
1688
1689 // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists.
1690 if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isOverflowOnly()) {
1691 // Determine which buffer the child should be in.
1692 QVector<RenderLayer *> *&buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
1693
1694 // Create the buffer if it doesn't exist yet.
1695 if (!buffer) {
1696 buffer = new QVector<RenderLayer *>();
1697 }
1698
1699 // Append ourselves at the end of the appropriate buffer.
1700 buffer->append(this);
1701 }
1702
1703 // Recur into our children to collect more layers, but only if we don't establish
1704 // a stacking context.
1705 if (m_hasVisibleDescendant && !isStackingContext()) {
1706 for (RenderLayer *child = firstChild(); child; child = child->nextSibling()) {
1707 child->collectLayers(posBuffer, negBuffer);
1708 }
1709 }
1710 }
1711
1712 #ifdef ENABLE_DUMP
operator <<(QTextStream & ts,const QRect & r)1713 static QTextStream &operator<<(QTextStream &ts, const QRect &r)
1714 {
1715 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
1716 }
1717
write(QTextStream & ts,RenderObject & o,const QString & indent)1718 static void write(QTextStream &ts, RenderObject &o, const QString &indent)
1719 {
1720 o.dump(ts, indent);
1721
1722 for (RenderObject *child = o.firstChild(); child; child = child->nextSibling()) {
1723 if (child->layer()) {
1724 continue;
1725 }
1726 write(ts, *child, indent + " ");
1727 }
1728 }
1729
write(QTextStream & ts,const RenderLayer & l,const QRect & layerBounds,const QRect & backgroundClipRect,const QRect & clipRect,int layerType=0,const QString & indent=QString ())1730 static void write(QTextStream &ts, const RenderLayer &l,
1731 const QRect &layerBounds, const QRect &backgroundClipRect, const QRect &clipRect,
1732 int layerType = 0, const QString &indent = QString())
1733
1734 {
1735 ts << indent << "layer";
1736
1737 ts << " at (" << l.xPos() << "," << l.yPos() << ") size " << l.width() << "x" << l.height();
1738
1739 if (layerBounds != layerBounds.intersected(backgroundClipRect)) {
1740 ts << " backgroundClip " << backgroundClipRect;
1741 }
1742 if (layerBounds != layerBounds.intersected(clipRect)) {
1743 ts << " clip " << clipRect;
1744 }
1745
1746 if (layerType == -1) {
1747 ts << " layerType: background only";
1748 } else if (layerType == 1) {
1749 ts << " layerType: foreground only";
1750 }
1751
1752 ts << "\n";
1753
1754 if (layerType != -1) {
1755 write(ts, *l.renderer(), indent + " ");
1756 }
1757
1758 ts << "\n";
1759 }
1760
writeLayers(QTextStream & ts,const RenderLayer * rootLayer,RenderLayer * l,const QRect & paintDirtyRect,const QString & indent)1761 static void writeLayers(QTextStream &ts, const RenderLayer *rootLayer, RenderLayer *l,
1762 const QRect &paintDirtyRect, const QString &indent)
1763 {
1764 // Calculate the clip rects we should use.
1765 QRect layerBounds, damageRect, clipRectToApply;
1766 l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
1767
1768 // Ensure our lists are up-to-date.
1769 l->updateZOrderLists();
1770 l->updateOverflowList();
1771
1772 bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
1773 QVector<RenderLayer *> *negList = l->negZOrderList();
1774 QVector<RenderLayer *> *ovfList = l->overflowList();
1775 if (shouldPaint && negList && negList->count() > 0) {
1776 write(ts, *l, layerBounds, damageRect, clipRectToApply, -1, indent);
1777 }
1778
1779 if (negList) {
1780 for (int i = 0; i != negList->count(); ++i) {
1781 writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent);
1782 }
1783 }
1784
1785 if (shouldPaint) {
1786 write(ts, *l, layerBounds, damageRect, clipRectToApply, negList && negList->count() > 0, indent);
1787 }
1788
1789 if (ovfList) {
1790 for (QVector<RenderLayer *>::iterator it = ovfList->begin(); it != ovfList->end(); ++it) {
1791 writeLayers(ts, rootLayer, *it, paintDirtyRect, indent);
1792 }
1793 }
1794
1795 QVector<RenderLayer *> *posList = l->posZOrderList();
1796 if (posList) {
1797 for (int i = 0; i != posList->count(); ++i) {
1798 writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
1799 }
1800 }
1801 }
1802
dump(QTextStream & ts,const QString & ind)1803 void RenderLayer::dump(QTextStream &ts, const QString &ind)
1804 {
1805 assert(renderer()->isCanvas());
1806
1807 writeLayers(ts, this, this, QRect(xPos(), yPos(), width(), height()), ind);
1808 }
1809
1810 #endif
1811
shouldBeOverflowOnly() const1812 bool RenderLayer::shouldBeOverflowOnly() const
1813 {
1814 return renderer()->style() && renderer()->hasOverflowClip() &&
1815 !renderer()->isPositioned() && !renderer()->isRelPositioned() && !isTransparent();
1816 }
1817
styleChanged()1818 void RenderLayer::styleChanged()
1819 {
1820 RenderLayer *parentSC = stackingContext();
1821
1822 // If we stopped being a stacking context, make sure to clear our
1823 // child lists so we don't end up with dangling references when a kid
1824 // is removed (as it wouldn't know to remove from us)
1825 bool nowStackingContext = isStackingContext();
1826 if (!nowStackingContext && (m_posZOrderList || m_negZOrderList)) {
1827 delete m_posZOrderList;
1828 m_posZOrderList = nullptr;
1829 delete m_negZOrderList;
1830 m_negZOrderList = nullptr;
1831 }
1832
1833 // If we stopped or started being a stacking context, dirty the parent, as
1834 // who is responsible for some of the layers may change
1835 if (nowStackingContext != m_wasStackingContext && parentSC) {
1836 parentSC->dirtyZOrderLists();
1837 }
1838
1839 m_wasStackingContext = nowStackingContext;
1840
1841 bool isOverflowOnly = shouldBeOverflowOnly();
1842 if (isOverflowOnly != m_isOverflowOnly) {
1843 m_isOverflowOnly = isOverflowOnly;
1844 RenderLayer *p = parent();
1845 if (p) {
1846 p->dirtyOverflowList();
1847 }
1848 if (parentSC) {
1849 parentSC->dirtyZOrderLists();
1850 }
1851 }
1852
1853 if (m_object->hasOverflowClip() &&
1854 m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
1855 if (!m_marquee) {
1856 m_marquee = new Marquee(this);
1857 }
1858 m_marquee->updateMarqueeStyle();
1859 } else if (m_marquee) {
1860 delete m_marquee;
1861 m_marquee = nullptr;
1862 }
1863 }
1864
suspendMarquees()1865 void RenderLayer::suspendMarquees()
1866 {
1867 if (m_marquee) {
1868 m_marquee->suspend();
1869 }
1870
1871 for (RenderLayer *curr = firstChild(); curr; curr = curr->nextSibling()) {
1872 curr->suspendMarquees();
1873 }
1874 }
1875
1876 // --------------------------------------------------------------------------
1877 // Marquee implementation
1878
Marquee(RenderLayer * l)1879 Marquee::Marquee(RenderLayer *l)
1880 : m_layer(l), m_currentLoop(0), m_totalLoops(0), m_timerId(0), m_start(0), m_end(0), m_speed(0), m_unfurlPos(0), m_reset(false),
1881 m_suspended(false), m_stopped(false), m_whiteSpace(NORMAL), m_direction(MAUTO)
1882 {
1883 }
1884
marqueeSpeed() const1885 int Marquee::marqueeSpeed() const
1886 {
1887 int result = m_layer->renderer()->style()->marqueeSpeed();
1888 DOM::NodeImpl *elt = m_layer->renderer()->element();
1889 if (elt && elt->id() == ID_MARQUEE) {
1890 HTMLMarqueeElementImpl *marqueeElt = static_cast<HTMLMarqueeElementImpl *>(elt);
1891 result = qMax(result, marqueeElt->minimumDelay());
1892 }
1893 return result;
1894 }
1895
direction() const1896 EMarqueeDirection Marquee::direction() const
1897 {
1898 // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
1899 // For now just map MAUTO to MBACKWARD
1900 EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
1901 EDirection dir = m_layer->renderer()->style()->direction();
1902 if (result == MAUTO) {
1903 result = MBACKWARD;
1904 }
1905 if (result == MFORWARD) {
1906 result = (dir == LTR) ? MRIGHT : MLEFT;
1907 }
1908 if (result == MBACKWARD) {
1909 result = (dir == LTR) ? MLEFT : MRIGHT;
1910 }
1911
1912 // Now we have the real direction. Next we check to see if the increment is negative.
1913 // If so, then we reverse the direction.
1914 Length increment = m_layer->renderer()->style()->marqueeIncrement();
1915 if (increment.isNegative()) {
1916 result = static_cast<EMarqueeDirection>(-result);
1917 }
1918
1919 return result;
1920 }
1921
isHorizontal() const1922 bool Marquee::isHorizontal() const
1923 {
1924 return direction() == MLEFT || direction() == MRIGHT;
1925 }
1926
isUnfurlMarquee() const1927 bool Marquee::isUnfurlMarquee() const
1928 {
1929 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1930 return (behavior == MUNFURL);
1931 }
1932
computePosition(EMarqueeDirection dir,bool stopAtContentEdge)1933 int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
1934 {
1935 RenderObject *o = m_layer->renderer();
1936 RenderStyle *s = o->style();
1937 if (isHorizontal()) {
1938 bool ltr = s->direction() == LTR;
1939 int clientWidth = o->clientWidth();
1940 int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
1941 if (ltr) {
1942 contentWidth += (o->paddingRight() - o->borderLeft());
1943 } else {
1944 contentWidth = o->width() - contentWidth;
1945 contentWidth += (o->paddingLeft() - o->borderRight());
1946 }
1947 if (dir == MRIGHT) {
1948 if (stopAtContentEdge) {
1949 return qMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1950 } else {
1951 return ltr ? contentWidth : clientWidth;
1952 }
1953 } else {
1954 if (stopAtContentEdge) {
1955 return qMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
1956 } else {
1957 return ltr ? -clientWidth : -contentWidth;
1958 }
1959 }
1960 } else {
1961 int contentHeight = m_layer->renderer()->lowestPosition(true, false) -
1962 m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
1963 int clientHeight = m_layer->renderer()->clientHeight();
1964 if (dir == MUP) {
1965 if (stopAtContentEdge) {
1966 return qMin(contentHeight - clientHeight, 0);
1967 } else {
1968 return -clientHeight;
1969 }
1970 } else {
1971 if (stopAtContentEdge) {
1972 return qMax(contentHeight - clientHeight, 0);
1973 } else {
1974 return contentHeight;
1975 }
1976 }
1977 }
1978 }
1979
start()1980 void Marquee::start()
1981 {
1982 if (m_timerId || m_layer->renderer()->style()->marqueeIncrement().isZero()) {
1983 return;
1984 }
1985
1986 if (!m_suspended && !m_stopped) {
1987 if (isUnfurlMarquee()) {
1988 bool forward = direction() == MDOWN || direction() == MRIGHT;
1989 bool isReversed = (forward && m_currentLoop % 2) || (!forward && !(m_currentLoop % 2));
1990 m_unfurlPos = isReversed ? m_end : m_start;
1991 m_layer->renderer()->setChildNeedsLayout(true);
1992 } else {
1993 if (isHorizontal()) {
1994 m_layer->scrollToOffset(m_start, 0, false, false, false);
1995 } else {
1996 m_layer->scrollToOffset(0, m_start, false, false, false);
1997 }
1998 }
1999 } else {
2000 m_suspended = false;
2001 }
2002
2003 m_stopped = false;
2004 m_timerId = startTimer(speed());
2005 }
2006
suspend()2007 void Marquee::suspend()
2008 {
2009 if (m_timerId) {
2010 killTimer(m_timerId);
2011 m_timerId = 0;
2012 }
2013
2014 m_suspended = true;
2015 }
2016
stop()2017 void Marquee::stop()
2018 {
2019 if (m_timerId) {
2020 killTimer(m_timerId);
2021 m_timerId = 0;
2022 }
2023
2024 m_stopped = true;
2025 }
2026
updateMarqueePosition()2027 void Marquee::updateMarqueePosition()
2028 {
2029 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
2030 if (activate) {
2031 if (isUnfurlMarquee()) {
2032 if (m_unfurlPos < m_start) {
2033 m_unfurlPos = m_start;
2034 m_layer->renderer()->setChildNeedsLayout(true);
2035 } else if (m_unfurlPos > m_end) {
2036 m_unfurlPos = m_end;
2037 m_layer->renderer()->setChildNeedsLayout(true);
2038 }
2039 } else {
2040 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
2041 m_start = computePosition(direction(), behavior == MALTERNATE);
2042 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
2043 }
2044 if (!m_stopped) {
2045 start();
2046 }
2047 }
2048 }
2049
updateMarqueeStyle()2050 void Marquee::updateMarqueeStyle()
2051 {
2052 RenderStyle *s = m_layer->renderer()->style();
2053
2054 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops)) {
2055 m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop.
2056 }
2057
2058 m_totalLoops = s->marqueeLoopCount();
2059 m_direction = s->marqueeDirection();
2060 m_whiteSpace = s->whiteSpace();
2061
2062 if (m_layer->renderer()->isHTMLMarquee()) {
2063 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
2064 // one loop.
2065 if (m_totalLoops <= 0 && (s->marqueeBehavior() == MSLIDE || s->marqueeBehavior() == MUNFURL)) {
2066 m_totalLoops = 1;
2067 }
2068
2069 // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
2070 // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate
2071 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect.
2072 // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the
2073 // marquee element.
2074 // FIXME: Bring these up with the CSS WG.
2075 if (isHorizontal() && m_layer->renderer()->childrenInline()) {
2076 s->setWhiteSpace(NOWRAP);
2077 s->setTextAlign(TAAUTO);
2078 }
2079 }
2080
2081 if (speed() != marqueeSpeed()) {
2082 m_speed = marqueeSpeed();
2083 if (m_timerId) {
2084 killTimer(m_timerId);
2085 m_timerId = startTimer(speed());
2086 }
2087 }
2088
2089 // Check the loop count to see if we should now stop.
2090 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
2091 if (activate && !m_timerId) {
2092 m_layer->renderer()->setNeedsLayout(true);
2093 } else if (!activate && m_timerId) {
2094 // Destroy the timer.
2095 killTimer(m_timerId);
2096 m_timerId = 0;
2097 }
2098 }
2099
timerEvent(QTimerEvent *)2100 void Marquee::timerEvent(QTimerEvent * /*evt*/)
2101 {
2102 if (m_layer->renderer()->needsLayout()) {
2103 return;
2104 }
2105
2106 if (m_reset) {
2107 m_reset = false;
2108 if (isHorizontal()) {
2109 m_layer->scrollToXOffset(m_start);
2110 } else {
2111 m_layer->scrollToYOffset(m_start);
2112 }
2113 return;
2114 }
2115
2116 RenderStyle *s = m_layer->renderer()->style();
2117
2118 int endPoint = m_end;
2119 int range = m_end - m_start;
2120 int newPos;
2121 if (range == 0) {
2122 newPos = m_end;
2123 } else {
2124 bool addIncrement = direction() == MUP || direction() == MLEFT;
2125 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2;
2126 if (isUnfurlMarquee()) {
2127 isReversed = (!addIncrement && m_currentLoop % 2) || (addIncrement && !(m_currentLoop % 2));
2128 addIncrement = !isReversed;
2129 }
2130 if (isReversed) {
2131 // We're going in the reverse direction.
2132 endPoint = m_start;
2133 range = -range;
2134 if (!isUnfurlMarquee()) {
2135 addIncrement = !addIncrement;
2136 }
2137 }
2138 bool positive = range > 0;
2139 int clientSize = isUnfurlMarquee() ? abs(range) :
2140 (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
2141 int increment = qMax(1, abs(m_layer->renderer()->style()->marqueeIncrement().width(clientSize)));
2142 int currentPos = isUnfurlMarquee() ? m_unfurlPos :
2143 (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset());
2144 newPos = currentPos + (addIncrement ? increment : -increment);
2145 if (positive) {
2146 newPos = qMin(newPos, endPoint);
2147 } else {
2148 newPos = qMax(newPos, endPoint);
2149 }
2150 }
2151
2152 if (newPos == endPoint) {
2153 m_currentLoop++;
2154 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) {
2155 killTimer(m_timerId);
2156 m_timerId = 0;
2157 } else if (s->marqueeBehavior() != MALTERNATE && s->marqueeBehavior() != MUNFURL) {
2158 m_reset = true;
2159 }
2160 }
2161
2162 if (isUnfurlMarquee()) {
2163 m_unfurlPos = newPos;
2164 m_layer->renderer()->setChildNeedsLayout(true);
2165 } else {
2166 if (isHorizontal()) {
2167 m_layer->scrollToXOffset(newPos);
2168 } else {
2169 m_layer->scrollToYOffset(newPos);
2170 }
2171 }
2172 }
2173
2174