15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/* 25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2011 Google Inc. All rights reserved. 35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without 55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions are 65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * met: 75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions of source code must retain the above copyright 95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * notice, this list of conditions and the following disclaimer. 105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Redistributions in binary form must reproduce the above 115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * copyright notice, this list of conditions and the following disclaimer 125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * in the documentation and/or other materials provided with the 135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * distribution. 145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * * Neither the name of Google Inc. nor the names of its 155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * contributors may be used to endorse or promote products derived from 165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * this software without specific prior written permission. 175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */ 305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h" 32f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu#include "web/painting/PaintAggregator.h" 33f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu 345267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "public/platform/Platform.h" 35f91f5fa1608c2cdd9af1842fb5dadbe78275be2aBo Liu 36c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)using namespace blink; 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3851b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)namespace blink { 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ---------------------------------------------------------------------------- 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ALGORITHM NOTES 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// We attempt to maintain a scroll rect in the presence of invalidations that 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// are contained within the scroll rect. If an invalidation crosses a scroll 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// rect, then we just treat the scroll rect as an invalidation rect. 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// For invalidations performed prior to scrolling and contained within the 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// scroll rect, we offset the invalidation rects to account for the fact that 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// the consumer will perform scrolling before painting. 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// We only support scrolling along one axis at a time. A diagonal scroll will 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// therefore be treated as an invalidation. 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ---------------------------------------------------------------------------- 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// If the combined area of paint rects contained within the scroll rect grows 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// too large, then we might as well just treat the scroll rect as a paint rect. 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// This constant sets the max ratio of paint rect area to scroll rect area that 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// we will tolerate before dograding the scroll into a repaint. 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const float maxRedundantPaintToScrollArea = 0.8f; 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// The maximum number of paint rects. If we exceed this limit, then we'll 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// start combining paint rects (see CombinePaintRects). This limiting is 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// important since the WebKit code associated with deciding what to paint given 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// a paint rect can be significant. 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const size_t maxPaintRects = 5; 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// If the combined area of paint rects divided by the area of the union of all 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// paint rects exceeds this threshold, then we will combine the paint rects. 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const float maxPaintRectsAreaRatio = 0.7f; 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static int calculateArea(const IntRect& rect) 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return rect.size().width() * rect.size().height(); 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Subtracts out the intersection of |a| and |b| from |a|, assuming |b| fully 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// overlaps with |a| in either the x- or y-direction. If there is no full 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// overlap, then |a| is returned. 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static IntRect subtractIntersection(const IntRect& a, const IntRect& b) 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // boundary cases: 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!a.intersects(b)) 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return a; 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.contains(a)) 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return IntRect(); 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rx = a.x(); 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int ry = a.y(); 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rr = a.maxX(); 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rb = a.maxY(); 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.y() <= a.y() && b.maxY() >= a.maxY()) { 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // complete intersection in the y-direction 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.x() <= a.x()) 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rx = b.maxX(); 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rr = b.x(); 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else if (b.x() <= a.x() && b.maxX() >= a.maxX()) { 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // complete intersection in the x-direction 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.y() <= a.y()) 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ry = b.maxY(); 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rb = b.y(); 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return IntRect(rx, ry, rr - rx, rb - ry); 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Returns true if |a| and |b| share an entire edge (i.e., same width or same 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// height), and the rectangles do not overlap. 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static bool sharesEdge(const IntRect& a, const IntRect& b) 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return (a.y() == b.y() && a.height() == b.height() && (a.x() == b.maxX() || a.maxX() == b.x())) 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) || (a.x() == b.x() && a.width() == b.width() && (a.y() == b.maxY() || a.maxY() == b.y())); 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)PaintAggregator::PendingUpdate::PendingUpdate() 1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)PaintAggregator::PendingUpdate::~PendingUpdate() 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::PendingUpdate::calculateScrollDamage() const 1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Should only be scrolling in one direction at a time. 1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ASSERT(!(scrollDelta.x() && scrollDelta.y())); 1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect damagedRect; 1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Compute the region we will expose by scrolling, and paint that into a 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // shared memory section. 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (scrollDelta.x()) { 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int dx = scrollDelta.x(); 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.y()); 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(scrollRect.height()); 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dx > 0) { 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.x()); 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(dx); 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.maxX() + dx); 1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(-dx); 1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int dy = scrollDelta.y(); 1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.x()); 1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(scrollRect.width()); 1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dy > 0) { 1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.y()); 1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(dy); 1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.maxY() + dy); 1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(-dy); 1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // In case the scroll offset exceeds the width/height of the scroll rect 1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return intersection(scrollRect, damagedRect); 1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::PendingUpdate::calculatePaintBounds() const 1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect bounds; 1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < paintRects.size(); ++i) 1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bounds.unite(paintRects[i]); 1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return bounds; 1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool PaintAggregator::hasPendingUpdate() const 1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return !m_update.scrollRect.isEmpty() || !m_update.paintRects.isEmpty(); 1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::clearPendingUpdate() 1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update = PendingUpdate(); 1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::popPendingUpdate(PendingUpdate* update) 1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine paint rects if their combined area is not sufficiently less than 1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // the area of the union of all paint rects. We skip this if there is a 1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // scroll rect since scrolling benefits from smaller paint rects. 1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.isEmpty() && m_update.paintRects.size() > 1) { 1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int paintArea = 0; 1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect unionRect; 1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paintArea += calculateArea(m_update.paintRects[i]); 1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unionRect.unite(m_update.paintRects[i]); 1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int unionArea = calculateArea(unionRect); 1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (float(paintArea) / float(unionArea) > maxPaintRectsAreaRatio) 1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) combinePaintRects(); 1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *update = m_update; 1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) clearPendingUpdate(); 1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::invalidateRect(const IntRect& rect) 2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine overlapping paints using smallest bounding box. 2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (existingRect.contains(rect)) // Optimize out redundancy. 2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (rect.intersects(existingRect) || sharesEdge(rect, existingRect)) { 2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Re-invalidate in case the union intersects other paint rects. 2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect combinedRect = unionRect(existingRect, rect); 2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(i); 2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(combinedRect); 2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Add a non-overlapping paint. 2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(rect); 2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // If the new paint overlaps with a scroll, then it forces an invalidation of 2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // the scroll. If the new paint is contained by a scroll, then trim off the 2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // scroll damage to avoid redundant painting. 2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.isEmpty()) { 2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (shouldInvalidateScrollRect(rect)) 2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else if (m_update.scrollRect.contains(rect)) { 2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects[m_update.paintRects.size() - 1] = 2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) subtractIntersection(rect, m_update.calculateScrollDamage()); 2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects[m_update.paintRects.size() - 1].isEmpty()) 2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(m_update.paintRects.size() - 1); 2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects.size() > maxPaintRects) 2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) combinePaintRects(); 2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Track how large the paintRects vector grows during an invalidation 2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // sequence. Note: A subsequent invalidation may end up being combined 2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // with all existing paints, which means that tracking the size of 2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // paintRects at the time when popPendingUpdate() is called may mask 2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // certain performance problems. 24051b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles) blink::Platform::current()->histogramCustomCounts("MPArch.RW_IntermediatePaintRectCount", 2415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.size(), 1, 100, 50); 2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::scrollRect(int dx, int dy, const IntRect& clipRect) 2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We only support scrolling along one axis at a time. 2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dx && dy) { 2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We can only scroll one rect at a time. 2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.isEmpty() && m_update.scrollRect != clipRect) { 2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Again, we only support scrolling along one axis at a time. Make sure this 2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // update doesn't scroll on a different axis than any existing one. 2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if ((dx && m_update.scrollDelta.y()) || (dy && m_update.scrollDelta.x())) { 2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The scroll rect is new or isn't changing (though the scroll amount may 2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // be changing). 2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = clipRect; 2685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollDelta.move(dx, dy); 2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We might have just wiped out a pre-existing scroll. 2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollDelta == IntPoint()) { 2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = IntRect(); 2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Adjust any contained paint rects and check for any overlapping paints. 2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(m_update.paintRects[i])) { 2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects[i] = scrollPaintRect(m_update.paintRects[i], dx, dy); 2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The rect may have been scrolled out of view. 2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects[i].isEmpty()) { 2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(i); 2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) i--; 2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else if (m_update.scrollRect.intersects(m_update.paintRects[i])) { 2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // If the new scroll overlaps too much with contained paint rects, then force 2925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // an invalidation of the scroll. 2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (shouldInvalidateScrollRect(IntRect())) 2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::scrollPaintRect(const IntRect& paintRect, int dx, int dy) const 2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect result = paintRect; 3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result.move(dx, dy); 3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result = intersection(m_update.scrollRect, result); 3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Subtract out the scroll damage rect to avoid redundant painting. 3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return subtractIntersection(result, m_update.calculateScrollDamage()); 3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool PaintAggregator::shouldInvalidateScrollRect(const IntRect& rect) const 3095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!rect.isEmpty()) { 3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.intersects(rect)) 3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.contains(rect)) 3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Check if the combined area of all contained paint rects plus this new 3195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // rect comes too close to the area of the scrollRect. If so, then we 3205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // might as well invalidate the scroll rect. 3215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int paintArea = calculateArea(rect); 3235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 3245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 3255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(existingRect)) 3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paintArea += calculateArea(existingRect); 3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int scrollArea = calculateArea(m_update.scrollRect); 3295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (float(paintArea) / float(scrollArea) > maxRedundantPaintToScrollArea) 3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 3315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::invalidateScrollRect() 3365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect scrollRect = m_update.scrollRect; 3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = IntRect(); 3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollDelta = IntPoint(); 3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(scrollRect); 3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::combinePaintRects() 3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine paint rects do to at most two rects: one inside the scrollRect 3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // and one outside the scrollRect. If there is no scrollRect, then just 3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // use the smallest bounding box for all paint rects. 3485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // 3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // NOTE: This is a fairly simple algorithm. We could get fancier by only 3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // combining two rects to get us under the maxPaintRects limit, but if we 3515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // reach this method then it means we're hitting a rare case, so there's no 3525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // need to over-optimize it. 3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // 3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.isEmpty()) { 3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect bounds = m_update.calculatePaintBounds(); 3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.clear(); 3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(bounds); 3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect inner, outer; 3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 3625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(existingRect)) 3635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) inner.unite(existingRect); 3645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 3655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) outer.unite(existingRect); 3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.clear(); 3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(inner); 3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(outer); 3705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 37351b2906e11752df6c18351cf520e30522d3b53a1Torne (Richard Coles)} // namespace blink 374