KHtml

render_layer.cpp
1 /*
2  * Copyright (C) 2003 Apple Computer, Inc.
3  * (C) 2006 Germain Garand <[email protected]>
4  * (C) 2006 Allan Sandfeld Jense <[email protected]>
5  *
6  * Portions are Copyright (C) 1998 Netscape Communications Corporation.
7  *
8  * Other contributors:
9  * Robert O'Callahan <[email protected]>
10  * David Baron <[email protected]>
11  * Christian Biesinger <[email protected]>
12  * Randall Jesup <[email protected]>
13  * Roland Mainz <[email protected]>
14  * Josh Soref <[email protected]>
15  * Boris Zbarsky <[email protected]>
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
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 
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 
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 
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 
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 
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 
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 
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 
314 void RenderLayer::dirtyVisibleContentStatus()
315 {
316  m_visibleContentStatusDirty = true;
317  if (parent()) {
318  parent()->dirtyVisibleDescendantStatus();
319  }
320 }
321 
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 
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 
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 
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 
461 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 
470 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 
479 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 
488 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 
498 bool RenderLayer::isTransparent() const
499 {
500  return m_object->style()->opacity() < 1.0f;
501 }
502 
503 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 
510 void *RenderLayer::operator new(size_t sz, RenderArena *renderArena) throw()
511 {
512  return renderArena->allocate(sz);
513 }
514 
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 
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 
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 
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 
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 
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 
655 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 
688 void RenderLayer::scrollOffset(int &x, int &y)
689 {
690  x += scrollXOffset();
691  y += scrollYOffset();
692 }
693 
694 void RenderLayer::subtractScrollOffset(int &x, int &y)
695 {
696  x -= scrollXOffset();
697  y -= scrollYOffset();
698 }
699 
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 
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 
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
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 
862 bool RenderLayer::hasReversedScrollbar() const
863 {
864  if (!m_vBar) {
865  return false;
866  }
867  return (m_vBar->layoutDirection() == Qt::RightToLeft);
868 }
869 
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 
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 
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 
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 
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 
1095 void RenderLayer::paint(QPainter *p, const QRect &damageRect, bool selectionOnly)
1096 {
1097  paintLayer(this, p, damageRect, selectionOnly);
1098 }
1099 
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 
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 {
1127  }
1128 }
1129 
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 
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 
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 
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 
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 
1459 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 
1467 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.
1475 static RenderObject *hoverAncestor(RenderObject *obj)
1476 {
1477  return (!obj->isInline() && obj->continuation()) ? obj->continuation() : obj->parent();
1478 }
1479 
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 
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).
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 
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 
1636 void RenderLayer::dirtyOverflowList()
1637 {
1638  if (m_overflowList) {
1639  m_overflowList->clear();
1640  }
1641  m_overflowListDirty = true;
1642 }
1643 
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 
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 
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
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 
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 
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 
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 
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 
1812 bool RenderLayer::shouldBeOverflowOnly() const
1813 {
1814  return renderer()->style() && renderer()->hasOverflowClip() &&
1815  !renderer()->isPositioned() && !renderer()->isRelPositioned() && !isTransparent();
1816 }
1817 
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 
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 
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 
1885 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 
1896 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 
1922 bool Marquee::isHorizontal() const
1923 {
1924  return direction() == MLEFT || direction() == MRIGHT;
1925 }
1926 
1927 bool Marquee::isUnfurlMarquee() const
1928 {
1929  EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
1930  return (behavior == MUNFURL);
1931 }
1932 
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 
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 
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 
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 
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 
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 
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 
bool hasClipping() const const
void append(const T &value)
QVector::iterator begin()
QWidget * widget() const const
This file is part of the HTML rendering engine for KDE.
void push(const T &t)
bool intersects(const QRect &rectangle) const const
MESSAGECORE_EXPORT KMime::Content * next(KMime::Content *node, bool allowChildren=true)
Renders and displays HTML in a QScrollArea.
Definition: khtmlview.h:97
int height() const const
int x() const const
int y() const const
void setClipRegion(const QRegion &region, Qt::ClipOperation operation)
RightToLeft
int x() const const
int y() const const
void drawRect(const QRectF &rectangle)
void clear()
QRegion clipRegion() const const
void setPen(const QColor &color)
int contentsHeight() const
Returns the contents area&#39;s height.
Definition: khtmlview.cpp:693
RenderObject * container() const
returns the object containing this one.
void setBrush(const QBrush &brush)
QRect intersected(const QRect &rectangle) const const
const QList< QKeySequence > & forward()
bool isEmpty() const const
bool isValid() const const
void move(int x, int y)
all geometry managing stuff is only in the block elements.
Definition: render_flow.h:44
WA_NoSystemBackground
This library provides a full-featured HTML parser and widget.
const T & at(int i) const const
QDataStream & operator<<(QDataStream &out, const KDateTime::Spec &spec)
bool contains(const QRect &rectangle, bool proper) const const
PM_ScrollBarExtent
int width() const const
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
bool isEmpty() const const
int count(const T &value) const const
Base Class for all rendering tree objects.
void replace(int i, const T &value)
Orientation
int contentsWidth() const
Returns the contents area&#39;s width.
Definition: khtmlview.cpp:688
QVector::iterator end()
qreal opacity() const const
T & top()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Tue Oct 26 2021 22:48:07 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.