|
1 /* |
|
2 * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. |
|
3 * |
|
4 * Portions are Copyright (C) 1998 Netscape Communications Corporation. |
|
5 * |
|
6 * Other contributors: |
|
7 * Robert O'Callahan <roc+@cs.cmu.edu> |
|
8 * David Baron <dbaron@fas.harvard.edu> |
|
9 * Christian Biesinger <cbiesinger@web.de> |
|
10 * Randall Jesup <rjesup@wgate.com> |
|
11 * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> |
|
12 * Josh Soref <timeless@mac.com> |
|
13 * Boris Zbarsky <bzbarsky@mit.edu> |
|
14 * |
|
15 * This library is free software; you can redistribute it and/or |
|
16 * modify it under the terms of the GNU Lesser General Public |
|
17 * License as published by the Free Software Foundation; either |
|
18 * version 2.1 of the License, or (at your option) any later version. |
|
19 * |
|
20 * This library is distributed in the hope that it will be useful, |
|
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
23 * Lesser General Public License for more details. |
|
24 * |
|
25 * You should have received a copy of the GNU Lesser General Public |
|
26 * License along with this library; if not, write to the Free Software |
|
27 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
28 * |
|
29 * Alternatively, the contents of this file may be used under the terms |
|
30 * of either the Mozilla Public License Version 1.1, found at |
|
31 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public |
|
32 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html |
|
33 * (the "GPL"), in which case the provisions of the MPL or the GPL are |
|
34 * applicable instead of those above. If you wish to allow use of your |
|
35 * version of this file only under the terms of one of those two |
|
36 * licenses (the MPL or the GPL) and not to allow others to use your |
|
37 * version of this file under the LGPL, indicate your decision by |
|
38 * deletingthe provisions above and replace them with the notice and |
|
39 * other provisions required by the MPL or the GPL, as the case may be. |
|
40 * If you do not delete the provisions above, a recipient may use your |
|
41 * version of this file under any of the LGPL, the MPL or the GPL. |
|
42 */ |
|
43 |
|
44 #include "config.h" |
|
45 #include "RenderLayer.h" |
|
46 |
|
47 #include "CSSPropertyNames.h" |
|
48 #include "Document.h" |
|
49 #include "EventHandler.h" |
|
50 #include "EventNames.h" |
|
51 #include "FloatRect.h" |
|
52 #include "Frame.h" |
|
53 #include "FrameView.h" |
|
54 #include "FrameTree.h" |
|
55 #include "GraphicsContext.h" |
|
56 #include "HTMLMarqueeElement.h" |
|
57 #include "HTMLNames.h" |
|
58 #include "HitTestRequest.h" |
|
59 #include "HitTestResult.h" |
|
60 #include "OverflowEvent.h" |
|
61 #include "PlatformMouseEvent.h" |
|
62 #include "PlatformScrollBar.h" |
|
63 #include "RenderArena.h" |
|
64 #include "RenderInline.h" |
|
65 #include "RenderTheme.h" |
|
66 #include "RenderView.h" |
|
67 #include "SelectionController.h" |
|
68 |
|
69 #if ENABLE(SVG) |
|
70 #include "SVGNames.h" |
|
71 #endif |
|
72 |
|
73 #define MIN_INTERSECT_FOR_REVEAL 32 |
|
74 |
|
75 using namespace std; |
|
76 |
|
77 namespace WebCore { |
|
78 |
|
79 using namespace EventNames; |
|
80 using namespace HTMLNames; |
|
81 |
|
82 #ifndef NDEBUG |
|
83 static bool inRenderLayerDestroy; |
|
84 #endif |
|
85 |
|
86 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge }; |
|
87 const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge }; |
|
88 const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter }; |
|
89 const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop }; |
|
90 const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom }; |
|
91 |
|
92 const int MinimumWidthWhileResizing = 100; |
|
93 const int MinimumHeightWhileResizing = 40; |
|
94 |
|
95 void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw() |
|
96 { |
|
97 return renderArena->allocate(sz); |
|
98 } |
|
99 |
|
100 void ClipRects::operator delete(void* ptr, size_t sz) |
|
101 { |
|
102 // Stash size where destroy can find it. |
|
103 *(size_t *)ptr = sz; |
|
104 } |
|
105 |
|
106 void ClipRects::destroy(RenderArena* renderArena) |
|
107 { |
|
108 delete this; |
|
109 |
|
110 // Recover the size left there for us by operator delete and free the memory. |
|
111 renderArena->free(*(size_t *)this, this); |
|
112 } |
|
113 |
|
114 RenderLayer::RenderLayer(RenderObject* object) |
|
115 : m_object(object) |
|
116 , m_parent(0) |
|
117 , m_previous(0) |
|
118 , m_next(0) |
|
119 , m_first(0) |
|
120 , m_last(0) |
|
121 , m_relX(0) |
|
122 , m_relY(0) |
|
123 , m_x(0) |
|
124 , m_y(0) |
|
125 , m_width(0) |
|
126 , m_height(0) |
|
127 , m_scrollX(0) |
|
128 , m_scrollY(0) |
|
129 , m_scrollOriginX(0) |
|
130 , m_scrollLeftOverflow(0) |
|
131 , m_scrollWidth(0) |
|
132 , m_scrollHeight(0) |
|
133 , m_inResizeMode(false) |
|
134 , m_posZOrderList(0) |
|
135 , m_negZOrderList(0) |
|
136 , m_overflowList(0) |
|
137 , m_clipRects(0) |
|
138 , m_scrollDimensionsDirty(true) |
|
139 , m_zOrderListsDirty(true) |
|
140 , m_overflowListDirty(true) |
|
141 , m_isOverflowOnly(shouldBeOverflowOnly()) |
|
142 , m_usedTransparency(false) |
|
143 , m_inOverflowRelayout(false) |
|
144 , m_needsFullRepaint(false) |
|
145 , m_overflowStatusDirty(true) |
|
146 , m_visibleContentStatusDirty(true) |
|
147 , m_hasVisibleContent(false) |
|
148 , m_visibleDescendantStatusDirty(false) |
|
149 , m_hasVisibleDescendant(false) |
|
150 , m_marquee(0) |
|
151 , m_staticX(0) |
|
152 , m_staticY(0) |
|
153 { |
|
154 if (!object->firstChild() && object->style()) { |
|
155 m_visibleContentStatusDirty = false; |
|
156 m_hasVisibleContent = object->style()->visibility() == VISIBLE; |
|
157 } |
|
158 } |
|
159 |
|
160 RenderLayer::~RenderLayer() |
|
161 { |
|
162 destroyScrollbar(HorizontalScrollbar); |
|
163 destroyScrollbar(VerticalScrollbar); |
|
164 |
|
165 // Child layers will be deleted by their corresponding render objects, so |
|
166 // we don't need to delete them ourselves. |
|
167 |
|
168 delete m_posZOrderList; |
|
169 delete m_negZOrderList; |
|
170 delete m_overflowList; |
|
171 delete m_marquee; |
|
172 |
|
173 // Make sure we have no lingering clip rects. |
|
174 ASSERT(!m_clipRects); |
|
175 } |
|
176 |
|
177 void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) |
|
178 { |
|
179 if (doFullRepaint) { |
|
180 m_object->repaint(); |
|
181 checkForRepaint = doFullRepaint = false; |
|
182 } |
|
183 |
|
184 updateLayerPosition(); // For relpositioned layers or non-positioned layers, |
|
185 // we need to keep in sync, since we may have shifted relative |
|
186 // to our parent layer. |
|
187 |
|
188 positionOverflowControls(); |
|
189 |
|
190 updateVisibilityStatus(); |
|
191 |
|
192 if (m_hasVisibleContent) { |
|
193 RenderView* view = m_object->view(); |
|
194 ASSERT(view); |
|
195 // FIXME: Optimize using LayoutState and remove the disableLayoutState() call |
|
196 // from updateScrollInfoAfterLayout(). |
|
197 ASSERT(!view->layoutState()); |
|
198 |
|
199 IntRect newRect = m_object->absoluteClippedOverflowRect(); |
|
200 IntRect newOutlineBox = m_object->absoluteOutlineBox(); |
|
201 if (checkForRepaint) { |
|
202 if (view && !view->printing()) { |
|
203 if (m_needsFullRepaint) { |
|
204 view->repaintViewRectangle(m_repaintRect); |
|
205 if (newRect != m_repaintRect) |
|
206 view->repaintViewRectangle(newRect); |
|
207 } else |
|
208 m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_outlineBox); |
|
209 } |
|
210 } |
|
211 m_repaintRect = newRect; |
|
212 m_outlineBox = newOutlineBox; |
|
213 } else { |
|
214 m_repaintRect = IntRect(); |
|
215 m_outlineBox = IntRect(); |
|
216 } |
|
217 |
|
218 m_needsFullRepaint = false; |
|
219 |
|
220 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
|
221 child->updateLayerPositions(doFullRepaint, checkForRepaint); |
|
222 |
|
223 // With all our children positioned, now update our marquee if we need to. |
|
224 if (m_marquee) |
|
225 m_marquee->updateMarqueePosition(); |
|
226 } |
|
227 |
|
228 void RenderLayer::setHasVisibleContent(bool b) |
|
229 { |
|
230 if (m_hasVisibleContent == b && !m_visibleContentStatusDirty) |
|
231 return; |
|
232 m_visibleContentStatusDirty = false; |
|
233 m_hasVisibleContent = b; |
|
234 if (m_hasVisibleContent) { |
|
235 m_repaintRect = renderer()->absoluteClippedOverflowRect(); |
|
236 m_outlineBox = renderer()->absoluteOutlineBox(); |
|
237 } |
|
238 if (parent()) |
|
239 parent()->childVisibilityChanged(m_hasVisibleContent); |
|
240 } |
|
241 |
|
242 void RenderLayer::dirtyVisibleContentStatus() |
|
243 { |
|
244 m_visibleContentStatusDirty = true; |
|
245 if (parent()) |
|
246 parent()->dirtyVisibleDescendantStatus(); |
|
247 } |
|
248 |
|
249 void RenderLayer::childVisibilityChanged(bool newVisibility) |
|
250 { |
|
251 if (m_hasVisibleDescendant == newVisibility || m_visibleDescendantStatusDirty) |
|
252 return; |
|
253 if (newVisibility) { |
|
254 RenderLayer* l = this; |
|
255 while (l && !l->m_visibleDescendantStatusDirty && !l->m_hasVisibleDescendant) { |
|
256 l->m_hasVisibleDescendant = true; |
|
257 l = l->parent(); |
|
258 } |
|
259 } else |
|
260 dirtyVisibleDescendantStatus(); |
|
261 } |
|
262 |
|
263 void RenderLayer::dirtyVisibleDescendantStatus() |
|
264 { |
|
265 RenderLayer* l = this; |
|
266 while (l && !l->m_visibleDescendantStatusDirty) { |
|
267 l->m_visibleDescendantStatusDirty = true; |
|
268 l = l->parent(); |
|
269 } |
|
270 } |
|
271 |
|
272 void RenderLayer::updateVisibilityStatus() |
|
273 { |
|
274 if (m_visibleDescendantStatusDirty) { |
|
275 m_hasVisibleDescendant = false; |
|
276 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
|
277 child->updateVisibilityStatus(); |
|
278 if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) { |
|
279 m_hasVisibleDescendant = true; |
|
280 break; |
|
281 } |
|
282 } |
|
283 m_visibleDescendantStatusDirty = false; |
|
284 } |
|
285 |
|
286 if (m_visibleContentStatusDirty) { |
|
287 if (m_object->style()->visibility() == VISIBLE) |
|
288 m_hasVisibleContent = true; |
|
289 else { |
|
290 // layer may be hidden but still have some visible content, check for this |
|
291 m_hasVisibleContent = false; |
|
292 RenderObject* r = m_object->firstChild(); |
|
293 while (r) { |
|
294 if (r->style()->visibility() == VISIBLE && !r->hasLayer()) { |
|
295 m_hasVisibleContent = true; |
|
296 break; |
|
297 } |
|
298 if (r->firstChild() && !r->hasLayer()) |
|
299 r = r->firstChild(); |
|
300 else if (r->nextSibling()) |
|
301 r = r->nextSibling(); |
|
302 else { |
|
303 do { |
|
304 r = r->parent(); |
|
305 if (r==m_object) |
|
306 r = 0; |
|
307 } while (r && !r->nextSibling()); |
|
308 if (r) |
|
309 r = r->nextSibling(); |
|
310 } |
|
311 } |
|
312 } |
|
313 m_visibleContentStatusDirty = false; |
|
314 } |
|
315 } |
|
316 |
|
317 void RenderLayer::updateLayerPosition() |
|
318 { |
|
319 // Clear our cached clip rect information. |
|
320 clearClipRect(); |
|
321 |
|
322 int x = m_object->xPos(); |
|
323 int y = m_object->yPos() - m_object->borderTopExtra(); |
|
324 |
|
325 if (!m_object->isPositioned() && m_object->parent()) { |
|
326 // We must adjust our position by walking up the render tree looking for the |
|
327 // nearest enclosing object with a layer. |
|
328 RenderObject* curr = m_object->parent(); |
|
329 while (curr && !curr->hasLayer()) { |
|
330 if (!curr->isTableRow()) { |
|
331 // Rows and cells share the same coordinate space (that of the section). |
|
332 // Omit them when computing our xpos/ypos. |
|
333 x += curr->xPos(); |
|
334 y += curr->yPos(); |
|
335 } |
|
336 curr = curr->parent(); |
|
337 } |
|
338 y += curr->borderTopExtra(); |
|
339 if (curr->isTableRow()) { |
|
340 // Put ourselves into the row coordinate space. |
|
341 x -= curr->xPos(); |
|
342 y -= curr->yPos(); |
|
343 } |
|
344 } |
|
345 |
|
346 m_relX = m_relY = 0; |
|
347 if (m_object->isRelPositioned()) { |
|
348 m_relX = static_cast<RenderBox*>(m_object)->relativePositionOffsetX(); |
|
349 m_relY = static_cast<RenderBox*>(m_object)->relativePositionOffsetY(); |
|
350 x += m_relX; y += m_relY; |
|
351 } |
|
352 |
|
353 // Subtract our parent's scroll offset. |
|
354 if (m_object->isPositioned() && enclosingPositionedAncestor()) { |
|
355 RenderLayer* positionedParent = enclosingPositionedAncestor(); |
|
356 |
|
357 // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. |
|
358 positionedParent->subtractScrollOffset(x, y); |
|
359 |
|
360 if (m_object->isPositioned()) { |
|
361 IntSize offset = static_cast<RenderBox*>(m_object)->offsetForPositionedInContainer(positionedParent->renderer()); |
|
362 x += offset.width(); |
|
363 y += offset.height(); |
|
364 } |
|
365 } else if (parent()) |
|
366 parent()->subtractScrollOffset(x, y); |
|
367 |
|
368 setPos(x,y); |
|
369 |
|
370 setWidth(m_object->width()); |
|
371 setHeight(m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra()); |
|
372 |
|
373 if (!m_object->hasOverflowClip()) { |
|
374 if (m_object->overflowWidth() > m_object->width()) |
|
375 setWidth(m_object->overflowWidth()); |
|
376 if (m_object->overflowHeight() > m_object->height()) |
|
377 setHeight(m_object->overflowHeight()); |
|
378 } |
|
379 } |
|
380 |
|
381 RenderLayer *RenderLayer::stackingContext() const |
|
382 { |
|
383 RenderLayer* curr = parent(); |
|
384 for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() && |
|
385 curr->m_object->style()->hasAutoZIndex(); |
|
386 curr = curr->parent()); |
|
387 return curr; |
|
388 } |
|
389 |
|
390 RenderLayer* |
|
391 RenderLayer::enclosingPositionedAncestor() const |
|
392 { |
|
393 RenderLayer* curr = parent(); |
|
394 for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() && |
|
395 !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned(); |
|
396 curr = curr->parent()); |
|
397 |
|
398 return curr; |
|
399 } |
|
400 |
|
401 bool |
|
402 RenderLayer::isTransparent() const |
|
403 { |
|
404 #if ENABLE(SVG) |
|
405 if (m_object->node()->namespaceURI() == SVGNames::svgNamespaceURI) |
|
406 return false; |
|
407 #endif |
|
408 return m_object->isTransparent(); |
|
409 } |
|
410 |
|
411 RenderLayer* |
|
412 RenderLayer::transparentAncestor() |
|
413 { |
|
414 RenderLayer* curr = parent(); |
|
415 for ( ; curr && !curr->isTransparent(); curr = curr->parent()); |
|
416 return curr; |
|
417 } |
|
418 |
|
419 static IntRect transparencyClipBox(RenderLayer* l) |
|
420 { |
|
421 // FIXME: Although this completely ignores clipping, we ultimately intersect with the |
|
422 // paintDirtyRect, and that should cut down on the amount we have to paint. Still it |
|
423 // would be better to respect clips. |
|
424 IntRect clipRect = l->absoluteBoundingBox(); |
|
425 |
|
426 // Note: we don't have to walk z-order lists since transparent elements always establish |
|
427 // a stacking context. This means we can just walk the layer tree directly. |
|
428 for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) |
|
429 clipRect.unite(transparencyClipBox(curr)); |
|
430 |
|
431 return clipRect; |
|
432 } |
|
433 |
|
434 void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const IntRect& paintDirtyRect) |
|
435 { |
|
436 if (p->paintingDisabled() || (isTransparent() && m_usedTransparency)) |
|
437 return; |
|
438 |
|
439 RenderLayer* ancestor = transparentAncestor(); |
|
440 if (ancestor) |
|
441 ancestor->beginTransparencyLayers(p, paintDirtyRect); |
|
442 |
|
443 if (isTransparent()) { |
|
444 m_usedTransparency = true; |
|
445 IntRect clipRect = transparencyClipBox(this); |
|
446 clipRect.intersect(paintDirtyRect); |
|
447 p->save(); |
|
448 p->clip(clipRect); |
|
449 p->beginTransparencyLayer(renderer()->opacity()); |
|
450 } |
|
451 } |
|
452 |
|
453 void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw() |
|
454 { |
|
455 return renderArena->allocate(sz); |
|
456 } |
|
457 |
|
458 void RenderLayer::operator delete(void* ptr, size_t sz) |
|
459 { |
|
460 ASSERT(inRenderLayerDestroy); |
|
461 |
|
462 // Stash size where destroy can find it. |
|
463 *(size_t *)ptr = sz; |
|
464 } |
|
465 |
|
466 void RenderLayer::destroy(RenderArena* renderArena) |
|
467 { |
|
468 #ifndef NDEBUG |
|
469 inRenderLayerDestroy = true; |
|
470 #endif |
|
471 delete this; |
|
472 #ifndef NDEBUG |
|
473 inRenderLayerDestroy = false; |
|
474 #endif |
|
475 |
|
476 // Recover the size left there for us by operator delete and free the memory. |
|
477 renderArena->free(*(size_t *)this, this); |
|
478 } |
|
479 |
|
480 void RenderLayer::addChild(RenderLayer *child, RenderLayer* beforeChild) |
|
481 { |
|
482 RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild(); |
|
483 if (prevSibling) { |
|
484 child->setPreviousSibling(prevSibling); |
|
485 prevSibling->setNextSibling(child); |
|
486 } |
|
487 else |
|
488 setFirstChild(child); |
|
489 |
|
490 if (beforeChild) { |
|
491 beforeChild->setPreviousSibling(child); |
|
492 child->setNextSibling(beforeChild); |
|
493 } |
|
494 else |
|
495 setLastChild(child); |
|
496 |
|
497 child->setParent(this); |
|
498 |
|
499 if (child->isOverflowOnly()) |
|
500 dirtyOverflowList(); |
|
501 else { |
|
502 // Dirty the z-order list in which we are contained. The stackingContext() can be null in the |
|
503 // case where we're building up generated content layers. This is ok, since the lists will start |
|
504 // off dirty in that case anyway. |
|
505 RenderLayer* stackingContext = child->stackingContext(); |
|
506 if (stackingContext) |
|
507 stackingContext->dirtyZOrderLists(); |
|
508 } |
|
509 |
|
510 child->updateVisibilityStatus(); |
|
511 if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) |
|
512 childVisibilityChanged(true); |
|
513 } |
|
514 |
|
515 RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) |
|
516 { |
|
517 // remove the child |
|
518 if (oldChild->previousSibling()) |
|
519 oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); |
|
520 if (oldChild->nextSibling()) |
|
521 oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); |
|
522 |
|
523 if (m_first == oldChild) |
|
524 m_first = oldChild->nextSibling(); |
|
525 if (m_last == oldChild) |
|
526 m_last = oldChild->previousSibling(); |
|
527 |
|
528 if (oldChild->isOverflowOnly()) |
|
529 dirtyOverflowList(); |
|
530 else { |
|
531 // Dirty the z-order list in which we are contained. When called via the |
|
532 // reattachment process in removeOnlyThisLayer, the layer may already be disconnected |
|
533 // from the main layer tree, so we need to null-check the |stackingContext| value. |
|
534 RenderLayer* stackingContext = oldChild->stackingContext(); |
|
535 if (stackingContext) |
|
536 stackingContext->dirtyZOrderLists(); |
|
537 } |
|
538 |
|
539 oldChild->setPreviousSibling(0); |
|
540 oldChild->setNextSibling(0); |
|
541 oldChild->setParent(0); |
|
542 |
|
543 oldChild->updateVisibilityStatus(); |
|
544 if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) |
|
545 childVisibilityChanged(false); |
|
546 |
|
547 return oldChild; |
|
548 } |
|
549 |
|
550 void RenderLayer::removeOnlyThisLayer() |
|
551 { |
|
552 if (!m_parent) |
|
553 return; |
|
554 |
|
555 // Dirty the clip rects. |
|
556 clearClipRects(); |
|
557 |
|
558 // Remove us from the parent. |
|
559 RenderLayer* parent = m_parent; |
|
560 RenderLayer* nextSib = nextSibling(); |
|
561 parent->removeChild(this); |
|
562 |
|
563 // Now walk our kids and reattach them to our parent. |
|
564 RenderLayer* current = m_first; |
|
565 while (current) { |
|
566 RenderLayer* next = current->nextSibling(); |
|
567 removeChild(current); |
|
568 parent->addChild(current, nextSib); |
|
569 current->updateLayerPositions(); |
|
570 current = next; |
|
571 } |
|
572 |
|
573 destroy(renderer()->renderArena()); |
|
574 } |
|
575 |
|
576 void RenderLayer::insertOnlyThisLayer() |
|
577 { |
|
578 if (!m_parent && renderer()->parent()) { |
|
579 // We need to connect ourselves when our renderer() has a parent. |
|
580 // Find our enclosingLayer and add ourselves. |
|
581 RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); |
|
582 if (parentLayer) |
|
583 parentLayer->addChild(this, |
|
584 renderer()->parent()->findNextLayer(parentLayer, renderer())); |
|
585 } |
|
586 |
|
587 // Remove all descendant layers from the hierarchy and add them to the new position. |
|
588 for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) |
|
589 curr->moveLayers(m_parent, this); |
|
590 |
|
591 // Clear out all the clip rects. |
|
592 clearClipRects(); |
|
593 } |
|
594 |
|
595 void |
|
596 RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const |
|
597 { |
|
598 if (ancestorLayer == this) |
|
599 return; |
|
600 |
|
601 if (m_object->style()->position() == FixedPosition) { |
|
602 // Add in the offset of the view. We can obtain this by calling |
|
603 // absolutePosition() on the RenderView. |
|
604 int xOff, yOff; |
|
605 m_object->absolutePosition(xOff, yOff, true); |
|
606 x += xOff; |
|
607 y += yOff; |
|
608 return; |
|
609 } |
|
610 |
|
611 RenderLayer* parentLayer; |
|
612 if (m_object->style()->position() == AbsolutePosition) |
|
613 parentLayer = enclosingPositionedAncestor(); |
|
614 else |
|
615 parentLayer = parent(); |
|
616 |
|
617 if (!parentLayer) return; |
|
618 |
|
619 parentLayer->convertToLayerCoords(ancestorLayer, x, y); |
|
620 |
|
621 x += xPos(); |
|
622 y += yPos(); |
|
623 } |
|
624 |
|
625 void |
|
626 RenderLayer::scrollOffset(int& x, int& y) |
|
627 { |
|
628 x += scrollXOffset() + m_scrollLeftOverflow; |
|
629 y += scrollYOffset(); |
|
630 } |
|
631 |
|
632 void |
|
633 RenderLayer::subtractScrollOffset(int& x, int& y) |
|
634 { |
|
635 x -= scrollXOffset() + m_scrollLeftOverflow; |
|
636 y -= scrollYOffset(); |
|
637 } |
|
638 |
|
639 void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint) |
|
640 { |
|
641 if (renderer()->style()->overflowX() != OMARQUEE) { |
|
642 if (x < 0) x = 0; |
|
643 if (y < 0) y = 0; |
|
644 |
|
645 // Call the scrollWidth/Height functions so that the dimensions will be computed if they need |
|
646 // to be (for overflow:hidden blocks). |
|
647 int maxX = scrollWidth() - m_object->clientWidth(); |
|
648 int maxY = scrollHeight() - m_object->clientHeight(); |
|
649 |
|
650 if (x > maxX) x = maxX; |
|
651 if (y > maxY) y = maxY; |
|
652 } |
|
653 |
|
654 // FIXME: Eventually, we will want to perform a blit. For now never |
|
655 // blit, since the check for blitting is going to be very |
|
656 // complicated (since it will involve testing whether our layer |
|
657 // is either occluded by another layer or clipped by an enclosing |
|
658 // layer or contains fixed backgrounds, etc.). |
|
659 m_scrollX = x - m_scrollOriginX; |
|
660 m_scrollY = y; |
|
661 |
|
662 // Update the positions of our child layers. |
|
663 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
|
664 child->updateLayerPositions(false, false); |
|
665 |
|
666 RenderView* view = renderer()->view(); |
|
667 |
|
668 // We should have a RenderView if we're trying to scroll. |
|
669 ASSERT(view); |
|
670 if (view) { |
|
671 // Update dashboard regions, scrolling may change the clip of a |
|
672 // particular region. |
|
673 view->frameView()->updateDashboardRegions(); |
|
674 |
|
675 view->updateWidgetPositions(); |
|
676 } |
|
677 |
|
678 // Just schedule a full repaint of our object. |
|
679 if (repaint) |
|
680 m_object->repaint(); |
|
681 |
|
682 if (updateScrollbars) { |
|
683 if (m_hBar) |
|
684 m_hBar->setValue(scrollXOffset()); |
|
685 if (m_vBar) |
|
686 m_vBar->setValue(m_scrollY); |
|
687 } |
|
688 |
|
689 // Schedule the scroll DOM event. |
|
690 if (view) |
|
691 if (FrameView* frameView = view->frameView()) |
|
692 frameView->scheduleEvent(new Event(scrollEvent, true, false), EventTargetNodeCast(renderer()->element()), true); |
|
693 } |
|
694 |
|
695 void RenderLayer::scrollRectToVisible(const IntRect &rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) |
|
696 { |
|
697 RenderLayer* parentLayer = 0; |
|
698 IntRect newRect = rect; |
|
699 int xOffset = 0, yOffset = 0; |
|
700 |
|
701 // We may end up propagating a scroll event. It is important that we suspend events until |
|
702 // the end of the function since they could delete the layer or the layer's m_object. |
|
703 FrameView* frameView = m_object->document()->view(); |
|
704 if (frameView) |
|
705 frameView->pauseScheduledEvents(); |
|
706 |
|
707 bool restrictedByLineClamp = false; |
|
708 if (m_object->parent()) { |
|
709 parentLayer = m_object->parent()->enclosingLayer(); |
|
710 restrictedByLineClamp = m_object->parent()->style()->lineClamp() >= 0; |
|
711 } |
|
712 |
|
713 if (m_object->hasOverflowClip() && !restrictedByLineClamp) { |
|
714 // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. |
|
715 // This will prevent us from revealing text hidden by the slider in Safari RSS. |
|
716 int x, y; |
|
717 m_object->absolutePosition(x, y); |
|
718 x += m_object->borderLeft(); |
|
719 y += m_object->borderTop(); |
|
720 |
|
721 IntRect layerBounds = IntRect(x + scrollXOffset(), y + scrollYOffset(), m_object->clientWidth(), m_object->clientHeight()); |
|
722 IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); |
|
723 IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); |
|
724 |
|
725 xOffset = r.x() - x; |
|
726 yOffset = r.y() - y; |
|
727 // Adjust offsets if they're outside of the allowable range. |
|
728 xOffset = max(0, min(scrollWidth() - layerBounds.width(), xOffset)); |
|
729 yOffset = max(0, min(scrollHeight() - layerBounds.height(), yOffset)); |
|
730 |
|
731 if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) { |
|
732 int diffX = scrollXOffset(); |
|
733 int diffY = scrollYOffset(); |
|
734 scrollToOffset(xOffset, yOffset); |
|
735 diffX = scrollXOffset() - diffX; |
|
736 diffY = scrollYOffset() - diffY; |
|
737 newRect.setX(rect.x() - diffX); |
|
738 newRect.setY(rect.y() - diffY); |
|
739 } |
|
740 } else if (!parentLayer) { |
|
741 if (frameView) { |
|
742 if (m_object->document() && m_object->document()->ownerElement() && m_object->document()->ownerElement()->renderer()) { |
|
743 IntRect viewRect = enclosingIntRect(frameView->visibleContentRect()); |
|
744 IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); |
|
745 |
|
746 xOffset = r.x(); |
|
747 yOffset = r.y(); |
|
748 // Adjust offsets if they're outside of the allowable range. |
|
749 xOffset = max(0, min(frameView->contentsWidth(), xOffset)); |
|
750 yOffset = max(0, min(frameView->contentsHeight(), yOffset)); |
|
751 |
|
752 frameView->setContentsPos(xOffset, yOffset); |
|
753 parentLayer = m_object->document()->ownerElement()->renderer()->enclosingLayer(); |
|
754 newRect.setX(rect.x() - frameView->contentsX() + frameView->x()); |
|
755 newRect.setY(rect.y() - frameView->contentsY() + frameView->y()); |
|
756 } else { |
|
757 IntRect viewRect = enclosingIntRect(frameView->visibleContentRectConsideringExternalScrollers()); |
|
758 IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); |
|
759 |
|
760 // If this is the outermost view that RenderLayer needs to scroll, then we should scroll the view recursively |
|
761 // Other apps, like Mail, rely on this feature. |
|
762 frameView->scrollRectIntoViewRecursively(r); |
|
763 } |
|
764 } |
|
765 } |
|
766 |
|
767 if (parentLayer) |
|
768 parentLayer->scrollRectToVisible(newRect, alignX, alignY); |
|
769 |
|
770 if (frameView) |
|
771 frameView->resumeScheduledEvents(); |
|
772 } |
|
773 |
|
774 IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) |
|
775 { |
|
776 // Determine the appropriate X behavior. |
|
777 ScrollBehavior scrollX; |
|
778 IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height()); |
|
779 int intersectWidth = intersection(visibleRect, exposeRectX).width(); |
|
780 if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) |
|
781 // If the rectangle is fully visible, use the specified visible behavior. |
|
782 // If the rectangle is partially visible, but over a certain threshold, |
|
783 // then treat it as fully visible to avoid unnecessary horizontal scrolling |
|
784 scrollX = getVisibleBehavior(alignX); |
|
785 else if (intersectWidth == visibleRect.width()) { |
|
786 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. |
|
787 scrollX = getVisibleBehavior(alignX); |
|
788 if (scrollX == alignCenter) |
|
789 scrollX = noScroll; |
|
790 } else if (intersectWidth > 0) |
|
791 // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior |
|
792 scrollX = getPartialBehavior(alignX); |
|
793 else |
|
794 scrollX = getHiddenBehavior(alignX); |
|
795 // If we're trying to align to the closest edge, and the exposeRect is further right |
|
796 // than the visibleRect, and not bigger than the visible area, then align with the right. |
|
797 if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width()) |
|
798 scrollX = alignRight; |
|
799 |
|
800 // Given the X behavior, compute the X coordinate. |
|
801 int x; |
|
802 if (scrollX == noScroll) |
|
803 x = visibleRect.x(); |
|
804 else if (scrollX == alignRight) |
|
805 x = exposeRect.right() - visibleRect.width(); |
|
806 else if (scrollX == alignCenter) |
|
807 x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2; |
|
808 else |
|
809 x = exposeRect.x(); |
|
810 |
|
811 // Determine the appropriate Y behavior. |
|
812 ScrollBehavior scrollY; |
|
813 IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); |
|
814 int intersectHeight = intersection(visibleRect, exposeRectY).height(); |
|
815 if (intersectHeight == exposeRect.height()) |
|
816 // If the rectangle is fully visible, use the specified visible behavior. |
|
817 scrollY = getVisibleBehavior(alignY); |
|
818 else if (intersectHeight == visibleRect.height()) { |
|
819 // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. |
|
820 scrollY = getVisibleBehavior(alignY); |
|
821 if (scrollY == alignCenter) |
|
822 scrollY = noScroll; |
|
823 } else if (intersectHeight > 0) |
|
824 // If the rectangle is partially visible, use the specified partial behavior |
|
825 scrollY = getPartialBehavior(alignY); |
|
826 else |
|
827 scrollY = getHiddenBehavior(alignY); |
|
828 // If we're trying to align to the closest edge, and the exposeRect is further down |
|
829 // than the visibleRect, and not bigger than the visible area, then align with the bottom. |
|
830 if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height()) |
|
831 scrollY = alignBottom; |
|
832 |
|
833 // Given the Y behavior, compute the Y coordinate. |
|
834 int y; |
|
835 if (scrollY == noScroll) |
|
836 y = visibleRect.y(); |
|
837 else if (scrollY == alignBottom) |
|
838 y = exposeRect.bottom() - visibleRect.height(); |
|
839 else if (scrollY == alignCenter) |
|
840 y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2; |
|
841 else |
|
842 y = exposeRect.y(); |
|
843 |
|
844 return IntRect(IntPoint(x, y), visibleRect.size()); |
|
845 } |
|
846 |
|
847 void RenderLayer::autoscroll() |
|
848 { |
|
849 Frame* frame = renderer()->document()->frame(); |
|
850 if (!frame) |
|
851 return; |
|
852 |
|
853 FrameView* frameView = frame->view(); |
|
854 if (!frameView) |
|
855 return; |
|
856 |
|
857 frame->eventHandler()->updateSelectionForMouseDrag(); |
|
858 |
|
859 IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); |
|
860 scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), gAlignToEdgeIfNeeded, gAlignToEdgeIfNeeded); |
|
861 } |
|
862 |
|
863 void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset) |
|
864 { |
|
865 if (!inResizeMode() || !m_object->hasOverflowClip()) |
|
866 return; |
|
867 |
|
868 // Set the width and height of the shadow ancestor node if there is one. |
|
869 // This is necessary for textarea elements since the resizable layer is in the shadow content. |
|
870 Element* element = static_cast<Element*>(m_object->node()->shadowAncestorNode()); |
|
871 RenderBox* renderer = static_cast<RenderBox*>(element->renderer()); |
|
872 |
|
873 EResize resize = renderer->style()->resize(); |
|
874 if (resize == RESIZE_NONE) |
|
875 return; |
|
876 |
|
877 Document* document = element->document(); |
|
878 if (!document->frame()->eventHandler()->mousePressed()) |
|
879 return; |
|
880 |
|
881 IntSize newOffset = offsetFromResizeCorner(document->view()->windowToContents(evt.pos())); |
|
882 |
|
883 IntSize currentSize = IntSize(renderer->width(), renderer->height()); |
|
884 |
|
885 IntSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize); |
|
886 element->setMinimumSizeForResizing(minimumSize); |
|
887 |
|
888 IntSize difference = (currentSize + newOffset - oldOffset).expandedTo(minimumSize) - currentSize; |
|
889 |
|
890 CSSStyleDeclaration* style = element->style(); |
|
891 bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX; |
|
892 |
|
893 ExceptionCode ec; |
|
894 |
|
895 if (difference.width()) { |
|
896 if (element && element->isControl()) { |
|
897 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). |
|
898 style->setProperty(CSS_PROP_MARGIN_LEFT, String::number(renderer->marginLeft()) + "px", false, ec); |
|
899 style->setProperty(CSS_PROP_MARGIN_RIGHT, String::number(renderer->marginRight()) + "px", false, ec); |
|
900 } |
|
901 int baseWidth = renderer->width() - (isBoxSizingBorder ? 0 |
|
902 : renderer->borderLeft() + renderer->paddingLeft() + renderer->borderRight() + renderer->paddingRight()); |
|
903 style->setProperty(CSS_PROP_WIDTH, String::number(baseWidth + difference.width()) + "px", false, ec); |
|
904 } |
|
905 |
|
906 if (difference.height()) { |
|
907 if (element && element->isControl()) { |
|
908 // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). |
|
909 style->setProperty(CSS_PROP_MARGIN_TOP, String::number(renderer->marginTop()) + "px", false, ec); |
|
910 style->setProperty(CSS_PROP_MARGIN_BOTTOM, String::number(renderer->marginBottom()) + "px", false, ec); |
|
911 } |
|
912 int baseHeight = renderer->height() - (isBoxSizingBorder ? 0 |
|
913 : renderer->borderTop() + renderer->paddingTop() + renderer->borderBottom() + renderer->paddingBottom()); |
|
914 style->setProperty(CSS_PROP_HEIGHT, String::number(baseHeight + difference.height()) + "px", false, ec); |
|
915 } |
|
916 |
|
917 document->updateLayout(); |
|
918 |
|
919 // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view. |
|
920 } |
|
921 |
|
922 PlatformScrollbar* RenderLayer::horizontalScrollbarWidget() const |
|
923 { |
|
924 if (m_hBar && m_hBar->isWidget()) |
|
925 return static_cast<PlatformScrollbar*>(m_hBar.get()); |
|
926 return 0; |
|
927 } |
|
928 |
|
929 PlatformScrollbar* RenderLayer::verticalScrollbarWidget() const |
|
930 { |
|
931 if (m_vBar && m_vBar->isWidget()) |
|
932 return static_cast<PlatformScrollbar*>(m_vBar.get()); |
|
933 return 0; |
|
934 } |
|
935 |
|
936 void RenderLayer::valueChanged(Scrollbar*) |
|
937 { |
|
938 // Update scroll position from scrollbars. |
|
939 |
|
940 bool needUpdate = false; |
|
941 int newX = scrollXOffset(); |
|
942 int newY = m_scrollY; |
|
943 |
|
944 if (m_hBar) { |
|
945 newX = m_hBar->value(); |
|
946 if (newX != scrollXOffset()) |
|
947 needUpdate = true; |
|
948 } |
|
949 |
|
950 if (m_vBar) { |
|
951 newY = m_vBar->value(); |
|
952 if (newY != m_scrollY) |
|
953 needUpdate = true; |
|
954 } |
|
955 |
|
956 if (needUpdate) |
|
957 scrollToOffset(newX, newY, false); |
|
958 } |
|
959 |
|
960 IntRect RenderLayer::windowClipRect() const |
|
961 { |
|
962 RenderView* view = renderer()->view(); |
|
963 ASSERT(view); |
|
964 FrameView* frameView = view->frameView(); |
|
965 if (!frameView) |
|
966 return IntRect(); |
|
967 return frameView->windowClipRectForLayer(this, false); |
|
968 } |
|
969 |
|
970 PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) |
|
971 { |
|
972 if (Scrollbar::hasPlatformScrollbars()) { |
|
973 RefPtr<PlatformScrollbar> widget = new PlatformScrollbar(this, orientation, RegularScrollbar); |
|
974 m_object->document()->view()->addChild(widget.get()); |
|
975 return widget.release(); |
|
976 } |
|
977 |
|
978 // FIXME: Create scrollbars using the engine. |
|
979 return 0; |
|
980 } |
|
981 |
|
982 void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) |
|
983 { |
|
984 RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar; |
|
985 if (scrollbar) { |
|
986 if (scrollbar->isWidget()) |
|
987 static_cast<PlatformScrollbar*>(scrollbar.get())->removeFromParent(); |
|
988 |
|
989 // FIXME: Destroy the engine scrollbar. |
|
990 scrollbar = 0; |
|
991 } |
|
992 } |
|
993 |
|
994 void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) |
|
995 { |
|
996 if (hasScrollbar == (m_hBar != 0)) |
|
997 return; |
|
998 |
|
999 if (hasScrollbar) |
|
1000 m_hBar = createScrollbar(HorizontalScrollbar); |
|
1001 else |
|
1002 destroyScrollbar(HorizontalScrollbar); |
|
1003 |
|
1004 // Force an update since we know the scrollbars have changed things. |
|
1005 if (m_object->document()->hasDashboardRegions()) |
|
1006 m_object->document()->setDashboardRegionsDirty(true); |
|
1007 } |
|
1008 |
|
1009 void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) |
|
1010 { |
|
1011 if (hasScrollbar == (m_vBar != 0)) |
|
1012 return; |
|
1013 |
|
1014 if (hasScrollbar) |
|
1015 m_vBar = createScrollbar(VerticalScrollbar); |
|
1016 else |
|
1017 destroyScrollbar(VerticalScrollbar); |
|
1018 |
|
1019 // Force an update since we know the scrollbars have changed things. |
|
1020 if (m_object->document()->hasDashboardRegions()) |
|
1021 m_object->document()->setDashboardRegionsDirty(true); |
|
1022 } |
|
1023 |
|
1024 int RenderLayer::verticalScrollbarWidth() const |
|
1025 { |
|
1026 if (!m_vBar) |
|
1027 return 0; |
|
1028 return m_vBar->width(); |
|
1029 } |
|
1030 |
|
1031 int RenderLayer::horizontalScrollbarHeight() const |
|
1032 { |
|
1033 if (!m_hBar) |
|
1034 return 0; |
|
1035 return m_hBar->height(); |
|
1036 } |
|
1037 |
|
1038 IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& p) const |
|
1039 { |
|
1040 // Currently the resize corner is always the bottom right corner |
|
1041 int x = width(); |
|
1042 int y = height(); |
|
1043 convertToLayerCoords(root(), x, y); |
|
1044 return p - IntPoint(x, y); |
|
1045 } |
|
1046 |
|
1047 static IntRect scrollCornerRect(RenderObject* renderer, const IntRect& absBounds) |
|
1048 { |
|
1049 int resizerWidth = PlatformScrollbar::verticalScrollbarWidth(); |
|
1050 int resizerHeight = PlatformScrollbar::horizontalScrollbarHeight(); |
|
1051 return IntRect(absBounds.right() - resizerWidth - renderer->style()->borderRightWidth(), |
|
1052 absBounds.bottom() - resizerHeight - renderer->style()->borderBottomWidth(), resizerWidth, resizerHeight); |
|
1053 } |
|
1054 |
|
1055 void RenderLayer::positionOverflowControls() |
|
1056 { |
|
1057 if (!m_hBar && !m_vBar && (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE)) |
|
1058 return; |
|
1059 |
|
1060 int x = 0; |
|
1061 int y = 0; |
|
1062 convertToLayerCoords(root(), x, y); |
|
1063 IntRect absBounds(x, y, m_object->width(), m_object->height()); |
|
1064 |
|
1065 IntRect resizeControlRect; |
|
1066 if (m_object->style()->resize() != RESIZE_NONE) |
|
1067 resizeControlRect = scrollCornerRect(m_object, absBounds); |
|
1068 |
|
1069 int resizeControlSize = max(resizeControlRect.height(), 0); |
|
1070 if (m_vBar) |
|
1071 m_vBar->setRect(IntRect(absBounds.right() - m_object->borderRight() - m_vBar->width(), |
|
1072 absBounds.y() + m_object->borderTop(), |
|
1073 m_vBar->width(), |
|
1074 absBounds.height() - (m_object->borderTop() + m_object->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize))); |
|
1075 |
|
1076 resizeControlSize = max(resizeControlRect.width(), 0); |
|
1077 if (m_hBar) |
|
1078 m_hBar->setRect(IntRect(absBounds.x() + m_object->borderLeft(), |
|
1079 absBounds.bottom() - m_object->borderBottom() - m_hBar->height(), |
|
1080 absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), |
|
1081 m_hBar->height())); |
|
1082 } |
|
1083 |
|
1084 int RenderLayer::scrollWidth() |
|
1085 { |
|
1086 if (m_scrollDimensionsDirty) |
|
1087 computeScrollDimensions(); |
|
1088 return m_scrollWidth; |
|
1089 } |
|
1090 |
|
1091 int RenderLayer::scrollHeight() |
|
1092 { |
|
1093 if (m_scrollDimensionsDirty) |
|
1094 computeScrollDimensions(); |
|
1095 return m_scrollHeight; |
|
1096 } |
|
1097 |
|
1098 void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) |
|
1099 { |
|
1100 m_scrollDimensionsDirty = false; |
|
1101 |
|
1102 bool ltr = m_object->style()->direction() == LTR; |
|
1103 |
|
1104 int clientWidth = m_object->clientWidth(); |
|
1105 int clientHeight = m_object->clientHeight(); |
|
1106 |
|
1107 m_scrollLeftOverflow = ltr ? 0 : min(0, m_object->leftmostPosition(true, false) - m_object->borderLeft()); |
|
1108 |
|
1109 int rightPos = ltr ? |
|
1110 m_object->rightmostPosition(true, false) - m_object->borderLeft() : |
|
1111 clientWidth - m_scrollLeftOverflow; |
|
1112 int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop(); |
|
1113 |
|
1114 m_scrollWidth = max(rightPos, clientWidth); |
|
1115 m_scrollHeight = max(bottomPos, clientHeight); |
|
1116 |
|
1117 m_scrollOriginX = ltr ? 0 : m_scrollWidth - clientWidth; |
|
1118 |
|
1119 if (needHBar) |
|
1120 *needHBar = rightPos > clientWidth; |
|
1121 if (needVBar) |
|
1122 *needVBar = bottomPos > clientHeight; |
|
1123 } |
|
1124 |
|
1125 void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow) |
|
1126 { |
|
1127 if (m_overflowStatusDirty) { |
|
1128 m_horizontalOverflow = horizontalOverflow; |
|
1129 m_verticalOverflow = verticalOverflow; |
|
1130 m_overflowStatusDirty = false; |
|
1131 |
|
1132 return; |
|
1133 } |
|
1134 |
|
1135 bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow); |
|
1136 bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow); |
|
1137 |
|
1138 if (horizontalOverflowChanged || verticalOverflowChanged) { |
|
1139 m_horizontalOverflow = horizontalOverflow; |
|
1140 m_verticalOverflow = verticalOverflow; |
|
1141 |
|
1142 if (FrameView* frameView = m_object->document()->view()) |
|
1143 frameView->scheduleEvent(new OverflowEvent(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), |
|
1144 EventTargetNodeCast(m_object->element()), true); |
|
1145 } |
|
1146 } |
|
1147 |
|
1148 void |
|
1149 RenderLayer::updateScrollInfoAfterLayout() |
|
1150 { |
|
1151 m_scrollDimensionsDirty = true; |
|
1152 |
|
1153 bool horizontalOverflow, verticalOverflow; |
|
1154 computeScrollDimensions(&horizontalOverflow, &verticalOverflow); |
|
1155 |
|
1156 if (m_object->style()->overflowX() != OMARQUEE) { |
|
1157 // Layout may cause us to be in an invalid scroll position. In this case we need |
|
1158 // to pull our scroll offsets back to the max (or push them up to the min). |
|
1159 int newX = max(0, min(scrollXOffset(), scrollWidth() - m_object->clientWidth())); |
|
1160 int newY = max(0, min(m_scrollY, scrollHeight() - m_object->clientHeight())); |
|
1161 if (newX != scrollXOffset() || newY != m_scrollY) { |
|
1162 RenderView* view = m_object->view(); |
|
1163 ASSERT(view); |
|
1164 // scrollToOffset() may call updateLayerPositions(), which doesn't work |
|
1165 // with LayoutState. |
|
1166 // FIXME: Remove the disableLayoutState/enableLayoutState if the above changes. |
|
1167 if (view) |
|
1168 view->disableLayoutState(); |
|
1169 scrollToOffset(newX, newY); |
|
1170 if (view) |
|
1171 view->enableLayoutState(); |
|
1172 } |
|
1173 } |
|
1174 |
|
1175 bool haveHorizontalBar = m_hBar; |
|
1176 bool haveVerticalBar = m_vBar; |
|
1177 |
|
1178 // overflow:scroll should just enable/disable. |
|
1179 if (m_object->style()->overflowX() == OSCROLL) |
|
1180 m_hBar->setEnabled(horizontalOverflow); |
|
1181 if (m_object->style()->overflowY() == OSCROLL) |
|
1182 m_vBar->setEnabled(verticalOverflow); |
|
1183 |
|
1184 // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any |
|
1185 // scrollbars that may be present. |
|
1186 if (m_object->style()->overflowX() == OHIDDEN && haveHorizontalBar) |
|
1187 setHasHorizontalScrollbar(false); |
|
1188 if (m_object->style()->overflowY() == OHIDDEN && haveVerticalBar) |
|
1189 setHasVerticalScrollbar(false); |
|
1190 |
|
1191 // overflow:auto may need to lay out again if scrollbars got added/removed. |
|
1192 bool scrollbarsChanged = (m_object->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || |
|
1193 (m_object->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); |
|
1194 if (scrollbarsChanged) { |
|
1195 if (m_object->hasAutoHorizontalScrollbar()) |
|
1196 setHasHorizontalScrollbar(horizontalOverflow); |
|
1197 if (m_object->hasAutoVerticalScrollbar()) |
|
1198 setHasVerticalScrollbar(verticalOverflow); |
|
1199 |
|
1200 // Force an update since we know the scrollbars have changed things. |
|
1201 if (m_object->document()->hasDashboardRegions()) |
|
1202 m_object->document()->setDashboardRegionsDirty(true); |
|
1203 |
|
1204 m_object->repaint(); |
|
1205 |
|
1206 if (m_object->style()->overflowX() == OAUTO || m_object->style()->overflowY() == OAUTO) { |
|
1207 if (!m_inOverflowRelayout) { |
|
1208 // Our proprietary overflow: overlay value doesn't trigger a layout. |
|
1209 m_inOverflowRelayout = true; |
|
1210 m_object->setNeedsLayout(true); |
|
1211 if (m_object->isRenderBlock()) |
|
1212 static_cast<RenderBlock*>(m_object)->layoutBlock(true); |
|
1213 else |
|
1214 m_object->layout(); |
|
1215 m_inOverflowRelayout = false; |
|
1216 } |
|
1217 } |
|
1218 } |
|
1219 |
|
1220 // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). |
|
1221 if (m_hBar && m_object->hasAutoHorizontalScrollbar()) |
|
1222 m_hBar->setEnabled(true); |
|
1223 if (m_vBar && m_object->hasAutoVerticalScrollbar()) |
|
1224 m_vBar->setEnabled(true); |
|
1225 |
|
1226 // Set up the range (and page step/line step). |
|
1227 if (m_hBar) { |
|
1228 int clientWidth = m_object->clientWidth(); |
|
1229 int pageStep = (clientWidth-PAGE_KEEP); |
|
1230 if (pageStep < 0) pageStep = clientWidth; |
|
1231 m_hBar->setSteps(LINE_STEP, pageStep); |
|
1232 m_hBar->setProportion(clientWidth, m_scrollWidth); |
|
1233 m_hBar->setValue(scrollXOffset()); |
|
1234 } |
|
1235 if (m_vBar) { |
|
1236 int clientHeight = m_object->clientHeight(); |
|
1237 int pageStep = (clientHeight-PAGE_KEEP); |
|
1238 if (pageStep < 0) pageStep = clientHeight; |
|
1239 m_vBar->setSteps(LINE_STEP, pageStep); |
|
1240 m_vBar->setProportion(clientHeight, m_scrollHeight); |
|
1241 m_object->repaintRectangle(IntRect(m_object->borderLeft() + m_object->clientWidth(), |
|
1242 m_object->borderTop(), verticalScrollbarWidth(), |
|
1243 m_object->height() - m_object->borderTop() - m_object->borderBottom())); |
|
1244 } |
|
1245 |
|
1246 if (m_object->element() && m_object->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) |
|
1247 updateOverflowStatus(horizontalOverflow, verticalOverflow); |
|
1248 } |
|
1249 |
|
1250 void RenderLayer::paintOverflowControls(GraphicsContext* p, int tx, int ty, const IntRect& damageRect) |
|
1251 { |
|
1252 // Don't do anything if we have no overflow. |
|
1253 if (!m_object->hasOverflowClip()) |
|
1254 return; |
|
1255 |
|
1256 // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout, but sometimes |
|
1257 // widgets can move without layout occurring (most notably when you scroll a document that |
|
1258 // contains fixed positioned elements). |
|
1259 positionOverflowControls(); |
|
1260 |
|
1261 // Now that we're sure the scrollbars are in the right place, paint them. |
|
1262 if (m_hBar) |
|
1263 m_hBar->paint(p, damageRect); |
|
1264 if (m_vBar) |
|
1265 m_vBar->paint(p, damageRect); |
|
1266 |
|
1267 // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the |
|
1268 // edge of the box. |
|
1269 IntRect paddingBox(m_object->xPos() + m_object->borderLeft() + tx, |
|
1270 m_object->yPos() + m_object->borderTop() + ty, |
|
1271 m_object->width() - m_object->borderLeft() - m_object->borderRight(), |
|
1272 m_object->height() - m_object->borderTop() - m_object->borderBottom()); |
|
1273 IntRect hCorner; |
|
1274 if (m_hBar && paddingBox.width() - m_hBar->width() > 0) { |
|
1275 hCorner = IntRect(paddingBox.x() + m_hBar->width(), |
|
1276 paddingBox.y() + paddingBox.height() - m_hBar->height(), |
|
1277 paddingBox.width() - m_hBar->width(), |
|
1278 m_hBar->height()); |
|
1279 if (hCorner.intersects(damageRect)) |
|
1280 p->fillRect(hCorner, Color::white); |
|
1281 } |
|
1282 if (m_vBar && paddingBox.height() - m_vBar->height() > 0) { |
|
1283 IntRect vCorner(paddingBox.x() + paddingBox.width() - m_vBar->width(), |
|
1284 paddingBox.y() + m_vBar->height(), |
|
1285 m_vBar->width(), |
|
1286 paddingBox.height() - m_vBar->height()); |
|
1287 if (vCorner != hCorner && vCorner.intersects(damageRect)) |
|
1288 p->fillRect(vCorner, Color::white); |
|
1289 } |
|
1290 |
|
1291 if (m_object->style()->resize() != RESIZE_NONE) { |
|
1292 IntRect absBounds(m_object->xPos() + tx, m_object->yPos() + ty, m_object->width(), m_object->height()); |
|
1293 IntRect scrollCorner = scrollCornerRect(m_object, absBounds); |
|
1294 if (!scrollCorner.intersects(damageRect)) |
|
1295 return; |
|
1296 |
|
1297 // Paint the resizer control. |
|
1298 static Image* resizeCornerImage; |
|
1299 if (!resizeCornerImage) |
|
1300 resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner"); |
|
1301 IntPoint imagePoint(scrollCorner.right() - resizeCornerImage->width(), scrollCorner.bottom() - resizeCornerImage->height()); |
|
1302 p->drawImage(resizeCornerImage, imagePoint); |
|
1303 |
|
1304 // Draw a frame around the resizer (1px grey line) if there are any scrollbars present. |
|
1305 // Clipping will exclude the right and bottom edges of this frame. |
|
1306 if (m_hBar || m_vBar) { |
|
1307 p->save(); |
|
1308 scrollCorner.setSize(IntSize(scrollCorner.width() + 1, scrollCorner.height() + 1)); |
|
1309 p->setStrokeColor(Color(makeRGB(217, 217, 217))); |
|
1310 p->setStrokeThickness(1.0f); |
|
1311 p->setFillColor(Color::transparent); |
|
1312 p->drawRect(scrollCorner); |
|
1313 p->restore(); |
|
1314 } |
|
1315 } |
|
1316 } |
|
1317 |
|
1318 bool RenderLayer::isPointInResizeControl(const IntPoint& point) |
|
1319 { |
|
1320 if (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE) |
|
1321 return false; |
|
1322 |
|
1323 int x = 0; |
|
1324 int y = 0; |
|
1325 convertToLayerCoords(root(), x, y); |
|
1326 IntRect absBounds(x, y, m_object->width(), m_object->height()); |
|
1327 return scrollCornerRect(m_object, absBounds).contains(point); |
|
1328 } |
|
1329 |
|
1330 bool RenderLayer::hitTestOverflowControls(HitTestResult& result) |
|
1331 { |
|
1332 if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) |
|
1333 return false; |
|
1334 |
|
1335 int x = 0; |
|
1336 int y = 0; |
|
1337 convertToLayerCoords(root(), x, y); |
|
1338 IntRect absBounds(x, y, renderer()->width(), renderer()->height()); |
|
1339 |
|
1340 IntRect resizeControlRect; |
|
1341 if (renderer()->style()->resize() != RESIZE_NONE) { |
|
1342 resizeControlRect = scrollCornerRect(renderer(), absBounds); |
|
1343 if (resizeControlRect.contains(result.point())) |
|
1344 return true; |
|
1345 } |
|
1346 |
|
1347 int resizeControlSize = max(resizeControlRect.height(), 0); |
|
1348 |
|
1349 if (m_vBar) { |
|
1350 IntRect vBarRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), absBounds.y() + renderer()->borderTop(), m_vBar->width(), absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); |
|
1351 if (vBarRect.contains(result.point())) { |
|
1352 result.setScrollbar(verticalScrollbarWidget()); |
|
1353 return true; |
|
1354 } |
|
1355 } |
|
1356 |
|
1357 resizeControlSize = max(resizeControlRect.width(), 0); |
|
1358 if (m_hBar) { |
|
1359 IntRect hBarRect(absBounds.x() + renderer()->borderLeft(), absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), m_hBar->height()); |
|
1360 if (hBarRect.contains(result.point())) { |
|
1361 result.setScrollbar(horizontalScrollbarWidget()); |
|
1362 return true; |
|
1363 } |
|
1364 } |
|
1365 |
|
1366 return false; |
|
1367 } |
|
1368 |
|
1369 bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) |
|
1370 { |
|
1371 bool didHorizontalScroll = false; |
|
1372 bool didVerticalScroll = false; |
|
1373 |
|
1374 if (m_hBar) { |
|
1375 if (granularity == ScrollByDocument) { |
|
1376 // Special-case for the ScrollByDocument granularity. A document scroll can only be up |
|
1377 // or down and in both cases the horizontal bar goes all the way to the left. |
|
1378 didHorizontalScroll = m_hBar->scroll(ScrollLeft, ScrollByDocument, multiplier); |
|
1379 } else |
|
1380 didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier); |
|
1381 } |
|
1382 |
|
1383 if (m_vBar) |
|
1384 didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier); |
|
1385 |
|
1386 return (didHorizontalScroll || didVerticalScroll); |
|
1387 } |
|
1388 |
|
1389 void |
|
1390 RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintRestriction paintRestriction, RenderObject *paintingRoot) |
|
1391 { |
|
1392 paintLayer(this, p, damageRect, false, paintRestriction, paintingRoot); |
|
1393 } |
|
1394 |
|
1395 static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) |
|
1396 { |
|
1397 // Work around bugs in focus ring clipping on Mac. |
|
1398 p->setFocusRingClip(clipRect); |
|
1399 if (paintDirtyRect == clipRect) |
|
1400 return; |
|
1401 p->save(); |
|
1402 p->clip(clipRect); |
|
1403 } |
|
1404 |
|
1405 static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) |
|
1406 { |
|
1407 // Work around bugs in focus ring clipping on Mac. |
|
1408 p->clearFocusRingClip(); |
|
1409 if (paintDirtyRect == clipRect) |
|
1410 return; |
|
1411 p->restore(); |
|
1412 } |
|
1413 |
|
1414 void |
|
1415 RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, |
|
1416 const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction, |
|
1417 RenderObject *paintingRoot) |
|
1418 { |
|
1419 // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC. |
|
1420 // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document |
|
1421 // will do a full repaint(). |
|
1422 if (renderer()->document()->didLayoutWithPendingStylesheets() && !renderer()->isRenderView() && !renderer()->isRoot()) |
|
1423 return; |
|
1424 |
|
1425 // Calculate the clip rects we should use. |
|
1426 IntRect layerBounds, damageRect, clipRectToApply, outlineRect; |
|
1427 calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect); |
|
1428 int x = layerBounds.x(); |
|
1429 int y = layerBounds.y(); |
|
1430 int tx = x - renderer()->xPos(); |
|
1431 int ty = y - renderer()->yPos() + renderer()->borderTopExtra(); |
|
1432 |
|
1433 // Ensure our lists are up-to-date. |
|
1434 updateZOrderLists(); |
|
1435 updateOverflowList(); |
|
1436 |
|
1437 // If this layer is totally invisible then there is nothing to paint. |
|
1438 if (!m_object->opacity()) |
|
1439 return; |
|
1440 |
|
1441 bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; |
|
1442 bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; |
|
1443 |
|
1444 if (isTransparent()) |
|
1445 haveTransparency = true; |
|
1446 |
|
1447 // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which |
|
1448 // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). |
|
1449 // Else, our renderer tree may or may not contain the painting root, so we pass that root along |
|
1450 // so it will be tested against as we decend through the renderers. |
|
1451 RenderObject *paintingRootForRenderer = 0; |
|
1452 if (paintingRoot && !m_object->isDescendantOf(paintingRoot)) |
|
1453 paintingRootForRenderer = paintingRoot; |
|
1454 |
|
1455 // We want to paint our layer, but only if we intersect the damage rect. |
|
1456 bool shouldPaint = intersectsDamageRect(layerBounds, damageRect) && m_hasVisibleContent; |
|
1457 if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) { |
|
1458 // Begin transparency layers lazily now that we know we have to paint something. |
|
1459 if (haveTransparency) |
|
1460 beginTransparencyLayers(p, paintDirtyRect); |
|
1461 |
|
1462 // Paint our background first, before painting any child layers. |
|
1463 // Establish the clip used to paint our background. |
|
1464 setClip(p, paintDirtyRect, damageRect); |
|
1465 |
|
1466 // Paint the background. |
|
1467 RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); |
|
1468 renderer()->paint(paintInfo, tx, ty); |
|
1469 |
|
1470 // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with |
|
1471 // z-index. We paint after we painted the background/border, so that the scrollbars will |
|
1472 // sit above the background/border. |
|
1473 paintOverflowControls(p, tx, ty, damageRect); |
|
1474 |
|
1475 // Restore the clip. |
|
1476 restoreClip(p, paintDirtyRect, damageRect); |
|
1477 } |
|
1478 |
|
1479 // Now walk the sorted list of children with negative z-indices. |
|
1480 if (m_negZOrderList) |
|
1481 for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it) |
|
1482 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); |
|
1483 |
|
1484 // Now establish the appropriate clip and paint our child RenderObjects. |
|
1485 if (shouldPaint && !clipRectToApply.isEmpty()) { |
|
1486 // Begin transparency layers lazily now that we know we have to paint something. |
|
1487 if (haveTransparency) |
|
1488 beginTransparencyLayers(p, paintDirtyRect); |
|
1489 |
|
1490 // Set up the clip used when painting our children. |
|
1491 setClip(p, paintDirtyRect, clipRectToApply); |
|
1492 RenderObject::PaintInfo paintInfo(p, clipRectToApply, |
|
1493 selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, |
|
1494 forceBlackText, paintingRootForRenderer, 0); |
|
1495 renderer()->paint(paintInfo, tx, ty); |
|
1496 if (!selectionOnly) { |
|
1497 paintInfo.phase = PaintPhaseFloat; |
|
1498 renderer()->paint(paintInfo, tx, ty); |
|
1499 paintInfo.phase = PaintPhaseForeground; |
|
1500 renderer()->paint(paintInfo, tx, ty); |
|
1501 paintInfo.phase = PaintPhaseChildOutlines; |
|
1502 renderer()->paint(paintInfo, tx, ty); |
|
1503 } |
|
1504 |
|
1505 // Now restore our clip. |
|
1506 restoreClip(p, paintDirtyRect, clipRectToApply); |
|
1507 } |
|
1508 |
|
1509 if (!outlineRect.isEmpty()) { |
|
1510 // Paint our own outline |
|
1511 RenderObject::PaintInfo paintInfo(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); |
|
1512 setClip(p, paintDirtyRect, outlineRect); |
|
1513 renderer()->paint(paintInfo, tx, ty); |
|
1514 restoreClip(p, paintDirtyRect, outlineRect); |
|
1515 } |
|
1516 |
|
1517 // Paint any child layers that have overflow. |
|
1518 if (m_overflowList) |
|
1519 for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it) |
|
1520 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); |
|
1521 |
|
1522 // Now walk the sorted list of children with positive z-indices. |
|
1523 if (m_posZOrderList) |
|
1524 for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it) |
|
1525 it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot); |
|
1526 |
|
1527 // End our transparency layer |
|
1528 if (isTransparent() && m_usedTransparency) { |
|
1529 p->endTransparencyLayer(); |
|
1530 p->restore(); |
|
1531 m_usedTransparency = false; |
|
1532 } |
|
1533 } |
|
1534 |
|
1535 static inline IntRect frameVisibleRect(RenderObject* renderer) |
|
1536 { |
|
1537 FrameView* frameView = renderer->document()->view(); |
|
1538 if (!frameView) |
|
1539 return IntRect(); |
|
1540 |
|
1541 return enclosingIntRect(frameView->visibleContentRect()); |
|
1542 } |
|
1543 |
|
1544 bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) |
|
1545 { |
|
1546 renderer()->document()->updateLayout(); |
|
1547 |
|
1548 IntRect boundsRect(m_x, m_y, width(), height()); |
|
1549 boundsRect.intersect(frameVisibleRect(renderer())); |
|
1550 |
|
1551 RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect); |
|
1552 |
|
1553 // Now determine if the result is inside an anchor; make sure an image map wins if |
|
1554 // it already set URLElement and only use the innermost. |
|
1555 Node* node = result.innerNode(); |
|
1556 while (node) { |
|
1557 // for imagemaps, URLElement is the associated area element not the image itself |
|
1558 if (node->isLink() && !result.URLElement() && !node->hasTagName(imgTag)) |
|
1559 result.setURLElement(static_cast<Element*>(node)); |
|
1560 node = node->parentNode(); |
|
1561 } |
|
1562 |
|
1563 // Next set up the correct :hover/:active state along the new chain. |
|
1564 updateHoverActiveState(request, result); |
|
1565 |
|
1566 // Now return whether we were inside this layer (this will always be true for the root |
|
1567 // layer). |
|
1568 return insideLayer; |
|
1569 } |
|
1570 |
|
1571 Node* RenderLayer::enclosingElement() const |
|
1572 { |
|
1573 for (RenderObject* r = renderer(); r; r = r->parent()) { |
|
1574 if (Node* e = r->element()) |
|
1575 return e; |
|
1576 } |
|
1577 ASSERT_NOT_REACHED(); |
|
1578 return 0; |
|
1579 } |
|
1580 |
|
1581 RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, |
|
1582 HitTestResult& result, const IntRect& hitTestRect) |
|
1583 { |
|
1584 // Calculate the clip rects we should use. |
|
1585 IntRect layerBounds; |
|
1586 IntRect bgRect; |
|
1587 IntRect fgRect; |
|
1588 IntRect outlineRect; |
|
1589 calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect); |
|
1590 |
|
1591 // Ensure our lists are up-to-date. |
|
1592 updateZOrderLists(); |
|
1593 updateOverflowList(); |
|
1594 |
|
1595 // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer, |
|
1596 // we are done and can return it. |
|
1597 RenderLayer* insideLayer = 0; |
|
1598 |
|
1599 // Begin by walking our list of positive layers from highest z-index down to the lowest |
|
1600 // z-index. |
|
1601 if (m_posZOrderList) { |
|
1602 for (int i = m_posZOrderList->size() - 1; i >= 0; --i) { |
|
1603 insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect); |
|
1604 if (insideLayer) |
|
1605 return insideLayer; |
|
1606 } |
|
1607 } |
|
1608 |
|
1609 // Now check our overflow objects. |
|
1610 if (m_overflowList) { |
|
1611 for (int i = m_overflowList->size() - 1; i >= 0; --i) { |
|
1612 insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect); |
|
1613 if (insideLayer) |
|
1614 return insideLayer; |
|
1615 } |
|
1616 } |
|
1617 |
|
1618 // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. |
|
1619 if (fgRect.contains(result.point()) && |
|
1620 renderer()->hitTest(request, result, result.point().x(), result.point().y(), |
|
1621 layerBounds.x() - renderer()->xPos(), |
|
1622 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), |
|
1623 HitTestDescendants)) { |
|
1624 // For positioned generated content, we might still not have a |
|
1625 // node by the time we get to the layer level, since none of |
|
1626 // the content in the layer has an element. So just walk up |
|
1627 // the tree. |
|
1628 if (!result.innerNode() || !result.innerNonSharedNode()) { |
|
1629 Node* e = enclosingElement(); |
|
1630 if (!result.innerNode()) |
|
1631 result.setInnerNode(e); |
|
1632 if (!result.innerNonSharedNode()) |
|
1633 result.setInnerNonSharedNode(e); |
|
1634 } |
|
1635 |
|
1636 return this; |
|
1637 } |
|
1638 |
|
1639 // Now check our negative z-index children. |
|
1640 if (m_negZOrderList) { |
|
1641 for (int i = m_negZOrderList->size() - 1; i >= 0; --i) { |
|
1642 insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect); |
|
1643 if (insideLayer) |
|
1644 return insideLayer; |
|
1645 } |
|
1646 } |
|
1647 |
|
1648 // Next we want to see if the mouse is inside this layer but not any of its children. |
|
1649 if (bgRect.contains(result.point()) && |
|
1650 renderer()->hitTest(request, result, result.point().x(), result.point().y(), |
|
1651 layerBounds.x() - renderer()->xPos(), |
|
1652 layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), |
|
1653 HitTestSelf)) { |
|
1654 if (!result.innerNode() || !result.innerNonSharedNode()) { |
|
1655 Node* e = enclosingElement(); |
|
1656 if (!result.innerNode()) |
|
1657 result.setInnerNode(e); |
|
1658 if (!result.innerNonSharedNode()) |
|
1659 result.setInnerNonSharedNode(e); |
|
1660 } |
|
1661 |
|
1662 return this; |
|
1663 } |
|
1664 |
|
1665 // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, |
|
1666 // return ourselves. We do this so mouse events continue getting delivered after a drag has |
|
1667 // exited the WebView, and so hit testing over a scrollbar hits the content document. |
|
1668 if ((request.active || request.mouseUp) && renderer()->isRenderView()) { |
|
1669 renderer()->updateHitTestResult(result, result.point()); |
|
1670 return this; |
|
1671 } |
|
1672 |
|
1673 return 0; |
|
1674 } |
|
1675 |
|
1676 void RenderLayer::calculateClipRects(const RenderLayer* rootLayer) |
|
1677 { |
|
1678 if (m_clipRects) |
|
1679 return; // We have the correct cached value. |
|
1680 |
|
1681 if (!parent()) { |
|
1682 // The root layer's clip rect is always just its dimensions. |
|
1683 m_clipRects = new (m_object->renderArena()) ClipRects(IntRect(0,0,width(),height())); |
|
1684 m_clipRects->ref(); |
|
1685 return; |
|
1686 } |
|
1687 |
|
1688 // Ensure that our parent's clip has been calculated so that we can examine the values. |
|
1689 parent()->calculateClipRects(rootLayer); |
|
1690 |
|
1691 // Set up our three rects to initially match the parent rects. |
|
1692 IntRect posClipRect(parent()->clipRects()->posClipRect()); |
|
1693 IntRect overflowClipRect(parent()->clipRects()->overflowClipRect()); |
|
1694 IntRect fixedClipRect(parent()->clipRects()->fixedClipRect()); |
|
1695 bool fixed = parent()->clipRects()->fixed(); |
|
1696 |
|
1697 // A fixed object is essentially the root of its containing block hierarchy, so when |
|
1698 // we encounter such an object, we reset our clip rects to the fixedClipRect. |
|
1699 if (m_object->style()->position() == FixedPosition) { |
|
1700 posClipRect = fixedClipRect; |
|
1701 overflowClipRect = fixedClipRect; |
|
1702 fixed = true; |
|
1703 } |
|
1704 else if (m_object->style()->position() == RelativePosition) |
|
1705 posClipRect = overflowClipRect; |
|
1706 else if (m_object->style()->position() == AbsolutePosition) |
|
1707 overflowClipRect = posClipRect; |
|
1708 |
|
1709 // Update the clip rects that will be passed to child layers. |
|
1710 if (m_object->hasOverflowClip() || m_object->hasClip()) { |
|
1711 // This layer establishes a clip of some kind. |
|
1712 int x = 0; |
|
1713 int y = 0; |
|
1714 convertToLayerCoords(rootLayer, x, y); |
|
1715 RenderView* view = renderer()->view(); |
|
1716 ASSERT(view); |
|
1717 if (view && fixed) { |
|
1718 x -= view->frameView()->contentsX(); |
|
1719 y -= view->frameView()->contentsY(); |
|
1720 } |
|
1721 |
|
1722 if (m_object->hasOverflowClip()) { |
|
1723 IntRect newOverflowClip = m_object->getOverflowClipRect(x,y); |
|
1724 overflowClipRect.intersect(newOverflowClip); |
|
1725 if (m_object->isPositioned() || m_object->isRelPositioned()) |
|
1726 posClipRect.intersect(newOverflowClip); |
|
1727 } |
|
1728 if (m_object->hasClip()) { |
|
1729 IntRect newPosClip = m_object->getClipRect(x,y); |
|
1730 posClipRect.intersect(newPosClip); |
|
1731 overflowClipRect.intersect(newPosClip); |
|
1732 fixedClipRect.intersect(newPosClip); |
|
1733 } |
|
1734 } |
|
1735 |
|
1736 // If our clip rects match our parent's clip, then we can just share its data structure and |
|
1737 // ref count. |
|
1738 if (fixed == parent()->clipRects()->fixed() && |
|
1739 posClipRect == parent()->clipRects()->posClipRect() && |
|
1740 overflowClipRect == parent()->clipRects()->overflowClipRect() && |
|
1741 fixedClipRect == parent()->clipRects()->fixedClipRect()) |
|
1742 m_clipRects = parent()->clipRects(); |
|
1743 else |
|
1744 m_clipRects = new (m_object->renderArena()) ClipRects(overflowClipRect, fixedClipRect, posClipRect, fixed); |
|
1745 m_clipRects->ref(); |
|
1746 } |
|
1747 |
|
1748 void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, |
|
1749 IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect) const |
|
1750 { |
|
1751 if (parent()) { |
|
1752 parent()->calculateClipRects(rootLayer); |
|
1753 |
|
1754 backgroundRect = m_object->style()->position() == FixedPosition ? parent()->clipRects()->fixedClipRect() : |
|
1755 (m_object->isPositioned() ? parent()->clipRects()->posClipRect() : |
|
1756 parent()->clipRects()->overflowClipRect()); |
|
1757 RenderView* view = renderer()->view(); |
|
1758 ASSERT(view); |
|
1759 if (view && parent()->clipRects()->fixed()) |
|
1760 backgroundRect.move(view->frameView()->contentsX(), view->frameView()->contentsY()); |
|
1761 |
|
1762 backgroundRect.intersect(paintDirtyRect); |
|
1763 } else |
|
1764 backgroundRect = paintDirtyRect; |
|
1765 foregroundRect = backgroundRect; |
|
1766 outlineRect = backgroundRect; |
|
1767 |
|
1768 int x = 0; |
|
1769 int y = 0; |
|
1770 convertToLayerCoords(rootLayer, x, y); |
|
1771 layerBounds = IntRect(x,y,width(),height()); |
|
1772 |
|
1773 // Update the clip rects that will be passed to child layers. |
|
1774 if (m_object->hasOverflowClip() || m_object->hasClip()) { |
|
1775 // This layer establishes a clip of some kind. |
|
1776 if (m_object->hasOverflowClip()) |
|
1777 foregroundRect.intersect(m_object->getOverflowClipRect(x,y)); |
|
1778 if (m_object->hasClip()) { |
|
1779 // Clip applies to *us* as well, so go ahead and update the damageRect. |
|
1780 IntRect newPosClip = m_object->getClipRect(x,y); |
|
1781 backgroundRect.intersect(newPosClip); |
|
1782 foregroundRect.intersect(newPosClip); |
|
1783 outlineRect.intersect(newPosClip); |
|
1784 } |
|
1785 |
|
1786 // If we establish a clip at all, then go ahead and make sure our background |
|
1787 // rect is intersected with our layer's bounds. |
|
1788 if (ShadowData* boxShadow = renderer()->style()->boxShadow()) { |
|
1789 IntRect shadowRect = layerBounds; |
|
1790 shadowRect.move(boxShadow->x, boxShadow->y); |
|
1791 shadowRect.inflate(boxShadow->blur); |
|
1792 shadowRect.unite(layerBounds); |
|
1793 backgroundRect.intersect(shadowRect); |
|
1794 } else |
|
1795 backgroundRect.intersect(layerBounds); |
|
1796 } |
|
1797 } |
|
1798 |
|
1799 IntRect RenderLayer::childrenClipRect() const |
|
1800 { |
|
1801 RenderLayer* rootLayer = renderer()->document()->renderer()->layer(); |
|
1802 IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; |
|
1803 calculateRects(rootLayer, rootLayer->absoluteBoundingBox(), layerBounds, backgroundRect, foregroundRect, outlineRect); |
|
1804 return foregroundRect; |
|
1805 } |
|
1806 |
|
1807 IntRect RenderLayer::selfClipRect() const |
|
1808 { |
|
1809 RenderLayer* rootLayer = renderer()->document()->renderer()->layer(); |
|
1810 IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; |
|
1811 calculateRects(rootLayer, rootLayer->absoluteBoundingBox(), layerBounds, backgroundRect, foregroundRect, outlineRect); |
|
1812 return backgroundRect; |
|
1813 } |
|
1814 |
|
1815 bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect) const |
|
1816 { |
|
1817 // Always examine the canvas and the root. |
|
1818 // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView |
|
1819 // paints the root's background. |
|
1820 if (renderer()->isRenderView() || renderer()->isRoot()) |
|
1821 return true; |
|
1822 |
|
1823 // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we |
|
1824 // can go ahead and return true. |
|
1825 RenderView* view = renderer()->view(); |
|
1826 ASSERT(view); |
|
1827 if (view && !renderer()->isInlineFlow()) { |
|
1828 IntRect b = layerBounds; |
|
1829 b.inflate(view->maximalOutlineSize()); |
|
1830 if (b.intersects(damageRect)) |
|
1831 return true; |
|
1832 } |
|
1833 |
|
1834 // Otherwise we need to compute the bounding box of this single layer and see if it intersects |
|
1835 // the damage rect. |
|
1836 return absoluteBoundingBox().intersects(damageRect); |
|
1837 } |
|
1838 |
|
1839 IntRect RenderLayer::absoluteBoundingBox() const |
|
1840 { |
|
1841 // There are three special cases we need to consider. |
|
1842 // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the |
|
1843 // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root |
|
1844 // line boxes of all three lines (including overflow on those lines). |
|
1845 // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top |
|
1846 // overflow, we have to create a bounding box that will extend to include this overflow. |
|
1847 // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats |
|
1848 // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those |
|
1849 // floats. |
|
1850 IntRect result; |
|
1851 if (renderer()->isInlineFlow()) { |
|
1852 // Go from our first line box to our last line box. |
|
1853 RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); |
|
1854 InlineFlowBox* firstBox = inlineFlow->firstLineBox(); |
|
1855 if (!firstBox) |
|
1856 return result; |
|
1857 int top = firstBox->root()->topOverflow(); |
|
1858 int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow(); |
|
1859 int left = firstBox->xPos(); |
|
1860 for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) |
|
1861 left = min(left, curr->xPos()); |
|
1862 result = IntRect(m_x + left, m_y + (top - renderer()->yPos()), width(), bottom - top); |
|
1863 } else if (renderer()->isTableRow()) { |
|
1864 // Our bounding box is just the union of all of our cells' border/overflow rects. |
|
1865 for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { |
|
1866 if (child->isTableCell()) { |
|
1867 IntRect bbox = child->borderBox(); |
|
1868 bbox.move(0, child->borderTopExtra()); |
|
1869 result.unite(bbox); |
|
1870 IntRect overflowRect = renderer()->overflowRect(false); |
|
1871 overflowRect.move(0, child->borderTopExtra()); |
|
1872 if (bbox != overflowRect) |
|
1873 result.unite(overflowRect); |
|
1874 } |
|
1875 } |
|
1876 result.move(m_x, m_y); |
|
1877 } else { |
|
1878 IntRect bbox = renderer()->borderBox(); |
|
1879 result = bbox; |
|
1880 IntRect overflowRect = renderer()->overflowRect(false); |
|
1881 if (bbox != overflowRect) |
|
1882 result.unite(overflowRect); |
|
1883 |
|
1884 // We have to adjust the x/y of this result so that it is in the coordinate space of the layer. |
|
1885 // We also have to add in borderTopExtra here, since borderBox(), in order to play well with methods like |
|
1886 // floatRect that deal with child content, uses an origin of (0,0) that is at the child content box (so |
|
1887 // border box returns a y coord of -borderTopExtra(). The layer, however, uses the outer box. This is all |
|
1888 // really confusing. |
|
1889 result.move(m_x, m_y + renderer()->borderTopExtra()); |
|
1890 } |
|
1891 |
|
1892 // Convert the bounding box to an absolute position. We can do this easily by looking at the delta |
|
1893 // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds |
|
1894 // passed in. |
|
1895 int absX = 0, absY = 0; |
|
1896 convertToLayerCoords(root(), absX, absY); |
|
1897 result.move(absX - m_x, absY - m_y); |
|
1898 RenderView* view = renderer()->view(); |
|
1899 ASSERT(view); |
|
1900 if (view) |
|
1901 result.inflate(view->maximalOutlineSize()); |
|
1902 return result; |
|
1903 } |
|
1904 |
|
1905 void RenderLayer::clearClipRects() |
|
1906 { |
|
1907 if (!m_clipRects) |
|
1908 return; |
|
1909 |
|
1910 clearClipRect(); |
|
1911 |
|
1912 for (RenderLayer* l = firstChild(); l; l = l->nextSibling()) |
|
1913 l->clearClipRects(); |
|
1914 } |
|
1915 |
|
1916 void RenderLayer::clearClipRect() |
|
1917 { |
|
1918 if (m_clipRects) { |
|
1919 m_clipRects->deref(m_object->renderArena()); |
|
1920 m_clipRects = 0; |
|
1921 } |
|
1922 } |
|
1923 |
|
1924 static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) |
|
1925 { |
|
1926 if (!obj1 || !obj2) |
|
1927 return 0; |
|
1928 |
|
1929 for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) |
|
1930 for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) |
|
1931 if (currObj1 == currObj2) |
|
1932 return currObj1; |
|
1933 |
|
1934 return 0; |
|
1935 } |
|
1936 |
|
1937 void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result) |
|
1938 { |
|
1939 // We don't update :hover/:active state when the result is marked as readonly. |
|
1940 if (request.readonly) |
|
1941 return; |
|
1942 |
|
1943 Document* doc = renderer()->document(); |
|
1944 |
|
1945 Node* activeNode = doc->activeNode(); |
|
1946 if (activeNode && !request.active) { |
|
1947 // We are clearing the :active chain because the mouse has been released. |
|
1948 for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) { |
|
1949 if (curr->element() && !curr->isText()) |
|
1950 curr->element()->setInActiveChain(false); |
|
1951 } |
|
1952 doc->setActiveNode(0); |
|
1953 } else { |
|
1954 Node* newActiveNode = result.innerNode(); |
|
1955 if (!activeNode && newActiveNode && request.active) { |
|
1956 // We are setting the :active chain and freezing it. If future moves happen, they |
|
1957 // will need to reference this chain. |
|
1958 for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) { |
|
1959 if (curr->element() && !curr->isText()) { |
|
1960 curr->element()->setInActiveChain(true); |
|
1961 } |
|
1962 } |
|
1963 doc->setActiveNode(newActiveNode); |
|
1964 } |
|
1965 } |
|
1966 |
|
1967 // If the mouse is down and if this is a mouse move event, we want to restrict changes in |
|
1968 // :hover/:active to only apply to elements that are in the :active chain that we froze |
|
1969 // at the time the mouse went down. |
|
1970 bool mustBeInActiveChain = request.active && request.mouseMove; |
|
1971 |
|
1972 // Check to see if the hovered node has changed. If not, then we don't need to |
|
1973 // do anything. |
|
1974 RefPtr<Node> oldHoverNode = doc->hoverNode(); |
|
1975 Node* newHoverNode = result.innerNode(); |
|
1976 |
|
1977 // Update our current hover node. |
|
1978 doc->setHoverNode(newHoverNode); |
|
1979 |
|
1980 // We have two different objects. Fetch their renderers. |
|
1981 RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0; |
|
1982 RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0; |
|
1983 |
|
1984 // Locate the common ancestor render object for the two renderers. |
|
1985 RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj); |
|
1986 |
|
1987 if (oldHoverObj != newHoverObj) { |
|
1988 // The old hover path only needs to be cleared up to (and not including) the common ancestor; |
|
1989 for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) { |
|
1990 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { |
|
1991 curr->element()->setActive(false); |
|
1992 curr->element()->setHovered(false); |
|
1993 } |
|
1994 } |
|
1995 } |
|
1996 |
|
1997 // Now set the hover state for our new object up to the root. |
|
1998 for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) { |
|
1999 if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { |
|
2000 curr->element()->setActive(request.active); |
|
2001 curr->element()->setHovered(true); |
|
2002 } |
|
2003 } |
|
2004 } |
|
2005 |
|
2006 // Helper for the sorting of layers by z-index. |
|
2007 static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) |
|
2008 { |
|
2009 return first->zIndex() < second->zIndex(); |
|
2010 } |
|
2011 |
|
2012 void RenderLayer::dirtyZOrderLists() |
|
2013 { |
|
2014 if (m_posZOrderList) |
|
2015 m_posZOrderList->clear(); |
|
2016 if (m_negZOrderList) |
|
2017 m_negZOrderList->clear(); |
|
2018 m_zOrderListsDirty = true; |
|
2019 } |
|
2020 |
|
2021 void RenderLayer::dirtyOverflowList() |
|
2022 { |
|
2023 if (m_overflowList) |
|
2024 m_overflowList->clear(); |
|
2025 m_overflowListDirty = true; |
|
2026 } |
|
2027 |
|
2028 void RenderLayer::updateZOrderLists() |
|
2029 { |
|
2030 if (!isStackingContext() || !m_zOrderListsDirty) |
|
2031 return; |
|
2032 |
|
2033 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
|
2034 child->collectLayers(m_posZOrderList, m_negZOrderList); |
|
2035 |
|
2036 // Sort the two lists. |
|
2037 if (m_posZOrderList) |
|
2038 std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); |
|
2039 if (m_negZOrderList) |
|
2040 std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); |
|
2041 |
|
2042 m_zOrderListsDirty = false; |
|
2043 } |
|
2044 |
|
2045 void RenderLayer::updateOverflowList() |
|
2046 { |
|
2047 if (!m_overflowListDirty) |
|
2048 return; |
|
2049 |
|
2050 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { |
|
2051 if (child->isOverflowOnly()) { |
|
2052 if (!m_overflowList) |
|
2053 m_overflowList = new Vector<RenderLayer*>; |
|
2054 m_overflowList->append(child); |
|
2055 } |
|
2056 } |
|
2057 |
|
2058 m_overflowListDirty = false; |
|
2059 } |
|
2060 |
|
2061 void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer) |
|
2062 { |
|
2063 updateVisibilityStatus(); |
|
2064 |
|
2065 // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. |
|
2066 if (m_hasVisibleContent && !isOverflowOnly()) { |
|
2067 // Determine which buffer the child should be in. |
|
2068 Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; |
|
2069 |
|
2070 // Create the buffer if it doesn't exist yet. |
|
2071 if (!buffer) |
|
2072 buffer = new Vector<RenderLayer*>; |
|
2073 |
|
2074 // Append ourselves at the end of the appropriate buffer. |
|
2075 buffer->append(this); |
|
2076 } |
|
2077 |
|
2078 // Recur into our children to collect more layers, but only if we don't establish |
|
2079 // a stacking context. |
|
2080 if (m_hasVisibleDescendant && !isStackingContext()) |
|
2081 for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) |
|
2082 child->collectLayers(posBuffer, negBuffer); |
|
2083 } |
|
2084 |
|
2085 void RenderLayer::repaintIncludingDescendants() |
|
2086 { |
|
2087 m_object->repaint(); |
|
2088 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) |
|
2089 curr->repaintIncludingDescendants(); |
|
2090 } |
|
2091 |
|
2092 bool RenderLayer::shouldBeOverflowOnly() const |
|
2093 { |
|
2094 return renderer()->hasOverflowClip() && |
|
2095 !renderer()->isPositioned() && |
|
2096 !renderer()->isRelPositioned() && |
|
2097 !isTransparent(); |
|
2098 } |
|
2099 |
|
2100 void RenderLayer::styleChanged() |
|
2101 { |
|
2102 bool isOverflowOnly = shouldBeOverflowOnly(); |
|
2103 if (isOverflowOnly != m_isOverflowOnly) { |
|
2104 m_isOverflowOnly = isOverflowOnly; |
|
2105 RenderLayer* p = parent(); |
|
2106 RenderLayer* sc = stackingContext(); |
|
2107 if (p) |
|
2108 p->dirtyOverflowList(); |
|
2109 if (sc) |
|
2110 sc->dirtyZOrderLists(); |
|
2111 } |
|
2112 |
|
2113 if (m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) { |
|
2114 if (!m_marquee) |
|
2115 m_marquee = new Marquee(this); |
|
2116 m_marquee->updateMarqueeStyle(); |
|
2117 } |
|
2118 else if (m_marquee) { |
|
2119 delete m_marquee; |
|
2120 m_marquee = 0; |
|
2121 } |
|
2122 } |
|
2123 |
|
2124 void RenderLayer::suspendMarquees() |
|
2125 { |
|
2126 if (m_marquee) |
|
2127 m_marquee->suspend(); |
|
2128 |
|
2129 for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) |
|
2130 curr->suspendMarquees(); |
|
2131 } |
|
2132 |
|
2133 // -------------------------------------------------------------------------- |
|
2134 // Marquee implementation |
|
2135 |
|
2136 Marquee::Marquee(RenderLayer* l) |
|
2137 : m_layer(l), m_currentLoop(0) |
|
2138 , m_totalLoops(0) |
|
2139 , m_timer(this, &Marquee::timerFired) |
|
2140 , m_start(0), m_end(0), m_speed(0), m_reset(false) |
|
2141 , m_suspended(false), m_stopped(false), m_direction(MAUTO) |
|
2142 { |
|
2143 } |
|
2144 |
|
2145 int Marquee::marqueeSpeed() const |
|
2146 { |
|
2147 int result = m_layer->renderer()->style()->marqueeSpeed(); |
|
2148 Node* elt = m_layer->renderer()->element(); |
|
2149 if (elt && elt->hasTagName(marqueeTag)) { |
|
2150 HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(elt); |
|
2151 result = max(result, marqueeElt->minimumDelay()); |
|
2152 } |
|
2153 return result; |
|
2154 } |
|
2155 |
|
2156 EMarqueeDirection Marquee::direction() const |
|
2157 { |
|
2158 // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee. |
|
2159 // For now just map MAUTO to MBACKWARD |
|
2160 EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection(); |
|
2161 TextDirection dir = m_layer->renderer()->style()->direction(); |
|
2162 if (result == MAUTO) |
|
2163 result = MBACKWARD; |
|
2164 if (result == MFORWARD) |
|
2165 result = (dir == LTR) ? MRIGHT : MLEFT; |
|
2166 if (result == MBACKWARD) |
|
2167 result = (dir == LTR) ? MLEFT : MRIGHT; |
|
2168 |
|
2169 // Now we have the real direction. Next we check to see if the increment is negative. |
|
2170 // If so, then we reverse the direction. |
|
2171 Length increment = m_layer->renderer()->style()->marqueeIncrement(); |
|
2172 if (increment.isNegative()) |
|
2173 result = static_cast<EMarqueeDirection>(-result); |
|
2174 |
|
2175 return result; |
|
2176 } |
|
2177 |
|
2178 bool Marquee::isHorizontal() const |
|
2179 { |
|
2180 return direction() == MLEFT || direction() == MRIGHT; |
|
2181 } |
|
2182 |
|
2183 int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge) |
|
2184 { |
|
2185 RenderObject* o = m_layer->renderer(); |
|
2186 RenderStyle* s = o->style(); |
|
2187 if (isHorizontal()) { |
|
2188 bool ltr = s->direction() == LTR; |
|
2189 int clientWidth = o->clientWidth(); |
|
2190 int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false); |
|
2191 if (ltr) |
|
2192 contentWidth += (o->paddingRight() - o->borderLeft()); |
|
2193 else { |
|
2194 contentWidth = o->width() - contentWidth; |
|
2195 contentWidth += (o->paddingLeft() - o->borderRight()); |
|
2196 } |
|
2197 if (dir == MRIGHT) { |
|
2198 if (stopAtContentEdge) |
|
2199 return max(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth)); |
|
2200 else |
|
2201 return ltr ? contentWidth : clientWidth; |
|
2202 } |
|
2203 else { |
|
2204 if (stopAtContentEdge) |
|
2205 return min(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth)); |
|
2206 else |
|
2207 return ltr ? -clientWidth : -contentWidth; |
|
2208 } |
|
2209 } |
|
2210 else { |
|
2211 int contentHeight = m_layer->renderer()->lowestPosition(true, false) - |
|
2212 m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom(); |
|
2213 int clientHeight = m_layer->renderer()->clientHeight(); |
|
2214 if (dir == MUP) { |
|
2215 if (stopAtContentEdge) |
|
2216 return min(contentHeight - clientHeight, 0); |
|
2217 else |
|
2218 return -clientHeight; |
|
2219 } |
|
2220 else { |
|
2221 if (stopAtContentEdge) |
|
2222 return max(contentHeight - clientHeight, 0); |
|
2223 else |
|
2224 return contentHeight; |
|
2225 } |
|
2226 } |
|
2227 } |
|
2228 |
|
2229 void Marquee::start() |
|
2230 { |
|
2231 if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().isZero()) |
|
2232 return; |
|
2233 |
|
2234 // We may end up propagating a scroll event. It is important that we suspend events until |
|
2235 // the end of the function since they could delete the layer, including the marquee. |
|
2236 FrameView* frameView = m_layer->renderer()->document()->view(); |
|
2237 if (frameView) |
|
2238 frameView->pauseScheduledEvents(); |
|
2239 |
|
2240 if (!m_suspended && !m_stopped) { |
|
2241 if (isHorizontal()) |
|
2242 m_layer->scrollToOffset(m_start, 0, false, false); |
|
2243 else |
|
2244 m_layer->scrollToOffset(0, m_start, false, false); |
|
2245 } |
|
2246 else { |
|
2247 m_suspended = false; |
|
2248 m_stopped = false; |
|
2249 } |
|
2250 |
|
2251 m_timer.startRepeating(speed() * 0.001); |
|
2252 |
|
2253 if (frameView) |
|
2254 frameView->resumeScheduledEvents(); |
|
2255 } |
|
2256 |
|
2257 void Marquee::suspend() |
|
2258 { |
|
2259 m_timer.stop(); |
|
2260 m_suspended = true; |
|
2261 } |
|
2262 |
|
2263 void Marquee::stop() |
|
2264 { |
|
2265 m_timer.stop(); |
|
2266 m_stopped = true; |
|
2267 } |
|
2268 |
|
2269 void Marquee::updateMarqueePosition() |
|
2270 { |
|
2271 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); |
|
2272 |
|
2273 #if PLATFORM(SYMBIAN) |
|
2274 // wap-marquee requests loop 0 as inactive marquee |
|
2275 if (m_totalLoops == 0 && m_layer->renderer()->style()->display() == WAP_MARQUEE) |
|
2276 activate = false; |
|
2277 #endif |
|
2278 |
|
2279 if (activate) { |
|
2280 EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior(); |
|
2281 m_start = computePosition(direction(), behavior == MALTERNATE); |
|
2282 m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE); |
|
2283 if (!m_stopped) |
|
2284 start(); |
|
2285 } |
|
2286 } |
|
2287 |
|
2288 void Marquee::updateMarqueeStyle() |
|
2289 { |
|
2290 RenderStyle* s = m_layer->renderer()->style(); |
|
2291 |
|
2292 if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops)) |
|
2293 m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop. |
|
2294 |
|
2295 m_totalLoops = s->marqueeLoopCount(); |
|
2296 m_direction = s->marqueeDirection(); |
|
2297 |
|
2298 if (m_layer->renderer()->isHTMLMarquee()) { |
|
2299 // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do |
|
2300 // one loop. |
|
2301 if (m_totalLoops <= 0 && s->marqueeBehavior() == MSLIDE) |
|
2302 m_totalLoops = 1; |
|
2303 |
|
2304 // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring |
|
2305 // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate |
|
2306 // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect. |
|
2307 // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the |
|
2308 // marquee element. |
|
2309 // FIXME: Bring these up with the CSS WG. |
|
2310 if (isHorizontal() && m_layer->renderer()->childrenInline()) { |
|
2311 s->setWhiteSpace(NOWRAP); |
|
2312 s->setTextAlign(TAAUTO); |
|
2313 } |
|
2314 } |
|
2315 |
|
2316 // Marquee height hack!! Make sure that, if it is a horizontal marquee, the height attribute is overridden |
|
2317 // if it is smaller than the font size. If it is a vertical marquee and height is not specified, we default |
|
2318 // to a marquee of 200px. |
|
2319 if (isHorizontal()) { |
|
2320 if (s->height().isFixed() && s->height().value() < s->fontSize()) |
|
2321 s->setHeight(Length(s->fontSize(),Fixed)); |
|
2322 } else if (s->height().isAuto()) //vertical marquee with no specified height |
|
2323 s->setHeight(Length(200, Fixed)); |
|
2324 |
|
2325 if (speed() != marqueeSpeed()) { |
|
2326 m_speed = marqueeSpeed(); |
|
2327 if (m_timer.isActive()) |
|
2328 m_timer.startRepeating(speed() * 0.001); |
|
2329 } |
|
2330 |
|
2331 // Check the loop count to see if we should now stop. |
|
2332 bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); |
|
2333 if (activate && !m_timer.isActive()) |
|
2334 m_layer->renderer()->setNeedsLayout(true); |
|
2335 else if (!activate && m_timer.isActive()) |
|
2336 m_timer.stop(); |
|
2337 } |
|
2338 |
|
2339 void Marquee::timerFired(Timer<Marquee>*) |
|
2340 { |
|
2341 if (m_layer->renderer()->needsLayout()) |
|
2342 return; |
|
2343 |
|
2344 if (m_reset) { |
|
2345 m_reset = false; |
|
2346 if (isHorizontal()) |
|
2347 m_layer->scrollToXOffset(m_start); |
|
2348 else |
|
2349 m_layer->scrollToYOffset(m_start); |
|
2350 return; |
|
2351 } |
|
2352 |
|
2353 RenderStyle* s = m_layer->renderer()->style(); |
|
2354 |
|
2355 int endPoint = m_end; |
|
2356 int range = m_end - m_start; |
|
2357 int newPos; |
|
2358 if (range == 0) |
|
2359 newPos = m_end; |
|
2360 else { |
|
2361 bool addIncrement = direction() == MUP || direction() == MLEFT; |
|
2362 bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2; |
|
2363 if (isReversed) { |
|
2364 // We're going in the reverse direction. |
|
2365 endPoint = m_start; |
|
2366 range = -range; |
|
2367 addIncrement = !addIncrement; |
|
2368 } |
|
2369 bool positive = range > 0; |
|
2370 int clientSize = (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight()); |
|
2371 int increment = max(1, abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize))); |
|
2372 int currentPos = (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset()); |
|
2373 newPos = currentPos + (addIncrement ? increment : -increment); |
|
2374 if (positive) |
|
2375 newPos = min(newPos, endPoint); |
|
2376 else |
|
2377 newPos = max(newPos, endPoint); |
|
2378 } |
|
2379 |
|
2380 if (newPos == endPoint) { |
|
2381 m_currentLoop++; |
|
2382 if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) |
|
2383 m_timer.stop(); |
|
2384 else if (s->marqueeBehavior() != MALTERNATE) |
|
2385 m_reset = true; |
|
2386 } |
|
2387 |
|
2388 if (isHorizontal()) |
|
2389 m_layer->scrollToXOffset(newPos); |
|
2390 else |
|
2391 m_layer->scrollToYOffset(newPos); |
|
2392 /* HLUU-7D3SDH: This special handling here is not compatible with 3.2 and 3.1 behavior. |
|
2393 #if PLATFORM(SYMBIAN) |
|
2394 if( m_totalLoops>0 && m_currentLoop >= m_totalLoops ) { |
|
2395 // -wap-marquee requires "the element MUST be rendered as if the 'display' property was set |
|
2396 // to block" after looping |
|
2397 if(s->display() == WAP_MARQUEE) { |
|
2398 s->setDisplay(BLOCK); |
|
2399 s->setOverflowX(OVISIBLE); |
|
2400 m_layer->renderer()->setHasOverflowClip( false ); |
|
2401 m_layer->styleChanged(); |
|
2402 } |
|
2403 } |
|
2404 #endif |
|
2405 */ |
|
2406 } |
|
2407 |
|
2408 } // namespace WebCore |