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" 325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "PaintAggregator.h" 335267f701546148b83dfbe1d151cb184385bb5c22Torne (Richard Coles)#include "public/platform/Platform.h" 345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using namespace WebCore; 355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)namespace WebKit { 375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ---------------------------------------------------------------------------- 395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ALGORITHM NOTES 405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// We attempt to maintain a scroll rect in the presence of invalidations that 425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// are contained within the scroll rect. If an invalidation crosses a scroll 435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// rect, then we just treat the scroll rect as an invalidation rect. 445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// For invalidations performed prior to scrolling and contained within the 465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// scroll rect, we offset the invalidation rects to account for the fact that 475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// the consumer will perform scrolling before painting. 485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// 495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// We only support scrolling along one axis at a time. A diagonal scroll will 505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// therefore be treated as an invalidation. 515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// ---------------------------------------------------------------------------- 525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// If the combined area of paint rects contained within the scroll rect grows 545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// too large, then we might as well just treat the scroll rect as a paint rect. 555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// This constant sets the max ratio of paint rect area to scroll rect area that 565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// we will tolerate before dograding the scroll into a repaint. 575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const float maxRedundantPaintToScrollArea = 0.8f; 585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// The maximum number of paint rects. If we exceed this limit, then we'll 605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// start combining paint rects (see CombinePaintRects). This limiting is 615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// important since the WebKit code associated with deciding what to paint given 625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// a paint rect can be significant. 635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const size_t maxPaintRects = 5; 645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// If the combined area of paint rects divided by the area of the union of all 665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// paint rects exceeds this threshold, then we will combine the paint rects. 675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static const float maxPaintRectsAreaRatio = 0.7f; 685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static int calculateArea(const IntRect& rect) 705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return rect.size().width() * rect.size().height(); 725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Subtracts out the intersection of |a| and |b| from |a|, assuming |b| fully 755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// overlaps with |a| in either the x- or y-direction. If there is no full 765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// overlap, then |a| is returned. 775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static IntRect subtractIntersection(const IntRect& a, const IntRect& b) 785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // boundary cases: 805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!a.intersects(b)) 815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return a; 825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.contains(a)) 835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return IntRect(); 845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rx = a.x(); 865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int ry = a.y(); 875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rr = a.maxX(); 885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int rb = a.maxY(); 895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.y() <= a.y() && b.maxY() >= a.maxY()) { 915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // complete intersection in the y-direction 925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.x() <= a.x()) 935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rx = b.maxX(); 945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rr = b.x(); 965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else if (b.x() <= a.x() && b.maxX() >= a.maxX()) { 975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // complete intersection in the x-direction 985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (b.y() <= a.y()) 995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ry = b.maxY(); 1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) rb = b.y(); 1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return IntRect(rx, ry, rr - rx, rb - ry); 1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// Returns true if |a| and |b| share an entire edge (i.e., same width or same 1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)// height), and the rectangles do not overlap. 1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static bool sharesEdge(const IntRect& a, const IntRect& b) 1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return (a.y() == b.y() && a.height() == b.height() && (a.x() == b.maxX() || a.maxX() == b.x())) 1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) || (a.x() == b.x() && a.width() == b.width() && (a.y() == b.maxY() || a.maxY() == b.y())); 1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)PaintAggregator::PendingUpdate::PendingUpdate() 1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)PaintAggregator::PendingUpdate::~PendingUpdate() 1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::PendingUpdate::calculateScrollDamage() const 1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Should only be scrolling in one direction at a time. 1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) ASSERT(!(scrollDelta.x() && scrollDelta.y())); 1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect damagedRect; 1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Compute the region we will expose by scrolling, and paint that into a 1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // shared memory section. 1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (scrollDelta.x()) { 1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int dx = scrollDelta.x(); 1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.y()); 1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(scrollRect.height()); 1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dx > 0) { 1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.x()); 1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(dx); 1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.maxX() + dx); 1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(-dx); 1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int dy = scrollDelta.y(); 1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setX(scrollRect.x()); 1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setWidth(scrollRect.width()); 1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dy > 0) { 1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.y()); 1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(dy); 1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setY(scrollRect.maxY() + dy); 1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) damagedRect.setHeight(-dy); 1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // In case the scroll offset exceeds the width/height of the scroll rect 1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return intersection(scrollRect, damagedRect); 1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::PendingUpdate::calculatePaintBounds() const 1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect bounds; 1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < paintRects.size(); ++i) 1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) bounds.unite(paintRects[i]); 1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return bounds; 1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool PaintAggregator::hasPendingUpdate() const 1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return !m_update.scrollRect.isEmpty() || !m_update.paintRects.isEmpty(); 1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::clearPendingUpdate() 1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update = PendingUpdate(); 1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::popPendingUpdate(PendingUpdate* update) 1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine paint rects if their combined area is not sufficiently less than 1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // the area of the union of all paint rects. We skip this if there is a 1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // scroll rect since scrolling benefits from smaller paint rects. 1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.isEmpty() && m_update.paintRects.size() > 1) { 1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int paintArea = 0; 1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect unionRect; 1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paintArea += calculateArea(m_update.paintRects[i]); 1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) unionRect.unite(m_update.paintRects[i]); 1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int unionArea = calculateArea(unionRect); 1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (float(paintArea) / float(unionArea) > maxPaintRectsAreaRatio) 1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) combinePaintRects(); 1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *update = m_update; 1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) clearPendingUpdate(); 1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::invalidateRect(const IntRect& rect) 1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine overlapping paints using smallest bounding box. 2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (existingRect.contains(rect)) // Optimize out redundancy. 2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (rect.intersects(existingRect) || sharesEdge(rect, existingRect)) { 2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Re-invalidate in case the union intersects other paint rects. 2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect combinedRect = unionRect(existingRect, rect); 2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(i); 2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(combinedRect); 2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Add a non-overlapping paint. 2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(rect); 2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // If the new paint overlaps with a scroll, then it forces an invalidation of 2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // the scroll. If the new paint is contained by a scroll, then trim off the 2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // scroll damage to avoid redundant painting. 2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.isEmpty()) { 2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (shouldInvalidateScrollRect(rect)) 2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else if (m_update.scrollRect.contains(rect)) { 2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects[m_update.paintRects.size() - 1] = 2245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) subtractIntersection(rect, m_update.calculateScrollDamage()); 2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects[m_update.paintRects.size() - 1].isEmpty()) 2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(m_update.paintRects.size() - 1); 2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects.size() > maxPaintRects) 2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) combinePaintRects(); 2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Track how large the paintRects vector grows during an invalidation 2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // sequence. Note: A subsequent invalidation may end up being combined 2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // with all existing paints, which means that tracking the size of 2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // paintRects at the time when popPendingUpdate() is called may mask 2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // certain performance problems. 2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) WebKit::Platform::current()->histogramCustomCounts("MPArch.RW_IntermediatePaintRectCount", 2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.size(), 1, 100, 50); 2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::scrollRect(int dx, int dy, const IntRect& clipRect) 2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We only support scrolling along one axis at a time. 2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (dx && dy) { 2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We can only scroll one rect at a time. 2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.isEmpty() && m_update.scrollRect != clipRect) { 2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Again, we only support scrolling along one axis at a time. Make sure this 2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // update doesn't scroll on a different axis than any existing one. 2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if ((dx && m_update.scrollDelta.y()) || (dy && m_update.scrollDelta.x())) { 2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(clipRect); 2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The scroll rect is new or isn't changing (though the scroll amount may 2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // be changing). 2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = clipRect; 2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollDelta.move(dx, dy); 2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // We might have just wiped out a pre-existing scroll. 2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollDelta == IntPoint()) { 2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = IntRect(); 2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Adjust any contained paint rects and check for any overlapping paints. 2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(m_update.paintRects[i])) { 2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects[i] = scrollPaintRect(m_update.paintRects[i], dx, dy); 2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // The rect may have been scrolled out of view. 2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.paintRects[i].isEmpty()) { 2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.remove(i); 2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) i--; 2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else if (m_update.scrollRect.intersects(m_update.paintRects[i])) { 2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return; 2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // If the new scroll overlaps too much with contained paint rects, then force 2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // an invalidation of the scroll. 2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (shouldInvalidateScrollRect(IntRect())) 2925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateScrollRect(); 2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)IntRect PaintAggregator::scrollPaintRect(const IntRect& paintRect, int dx, int dy) const 2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect result = paintRect; 2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result.move(dx, dy); 3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) result = intersection(m_update.scrollRect, result); 3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Subtract out the scroll damage rect to avoid redundant painting. 3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return subtractIntersection(result, m_update.calculateScrollDamage()); 3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)bool PaintAggregator::shouldInvalidateScrollRect(const IntRect& rect) const 3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!rect.isEmpty()) { 3095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.intersects(rect)) 3105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (!m_update.scrollRect.contains(rect)) 3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Check if the combined area of all contained paint rects plus this new 3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // rect comes too close to the area of the scrollRect. If so, then we 3185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // might as well invalidate the scroll rect. 3195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int paintArea = calculateArea(rect); 3215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 3225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 3235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(existingRect)) 3245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) paintArea += calculateArea(existingRect); 3255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) int scrollArea = calculateArea(m_update.scrollRect); 3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (float(paintArea) / float(scrollArea) > maxRedundantPaintToScrollArea) 3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return true; 3295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) return false; 3315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::invalidateScrollRect() 3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect scrollRect = m_update.scrollRect; 3365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollRect = IntRect(); 3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.scrollDelta = IntPoint(); 3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) invalidateRect(scrollRect); 3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void PaintAggregator::combinePaintRects() 3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){ 3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // Combine paint rects do to at most two rects: one inside the scrollRect 3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // and one outside the scrollRect. If there is no scrollRect, then just 3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // use the smallest bounding box for all paint rects. 3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // 3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // NOTE: This is a fairly simple algorithm. We could get fancier by only 3485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // combining two rects to get us under the maxPaintRects limit, but if we 3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // reach this method then it means we're hitting a rare case, so there's no 3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // need to over-optimize it. 3515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) // 3525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.isEmpty()) { 3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect bounds = m_update.calculatePaintBounds(); 3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.clear(); 3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(bounds); 3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } else { 3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) IntRect inner, outer; 3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) for (size_t i = 0; i < m_update.paintRects.size(); ++i) { 3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) const IntRect& existingRect = m_update.paintRects[i]; 3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) if (m_update.scrollRect.contains(existingRect)) 3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) inner.unite(existingRect); 3625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) else 3635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) outer.unite(existingRect); 3645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.clear(); 3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(inner); 3675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) m_update.paintRects.append(outer); 3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) } 3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} 3705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) 3715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)} // namespace WebKit 372