1 /*
2 * Copyright (C) 2011 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "DrawingAreaImpl.h"
28
29 #include "DrawingAreaProxyMessages.h"
30 #include "LayerTreeContext.h"
31 #include "ShareableBitmap.h"
32 #include "UpdateInfo.h"
33 #include "WebPage.h"
34 #include "WebPageCreationParameters.h"
35 #include "WebProcess.h"
36 #include <WebCore/GraphicsContext.h>
37 #include <WebCore/Page.h>
38 #include <WebCore/Settings.h>
39
40 #if !PLATFORM(MAC) && !PLATFORM(WIN)
41 #error "This drawing area is not ready for use by other ports yet."
42 #endif
43
44 using namespace WebCore;
45 using namespace std;
46
47 namespace WebKit {
48
create(WebPage * webPage,const WebPageCreationParameters & parameters)49 PassOwnPtr<DrawingAreaImpl> DrawingAreaImpl::create(WebPage* webPage, const WebPageCreationParameters& parameters)
50 {
51 return adoptPtr(new DrawingAreaImpl(webPage, parameters));
52 }
53
~DrawingAreaImpl()54 DrawingAreaImpl::~DrawingAreaImpl()
55 {
56 if (m_layerTreeHost)
57 m_layerTreeHost->invalidate();
58 }
59
DrawingAreaImpl(WebPage * webPage,const WebPageCreationParameters & parameters)60 DrawingAreaImpl::DrawingAreaImpl(WebPage* webPage, const WebPageCreationParameters& parameters)
61 : DrawingArea(DrawingAreaTypeImpl, webPage)
62 , m_backingStoreStateID(0)
63 , m_inUpdateBackingStoreState(false)
64 , m_shouldSendDidUpdateBackingStoreState(false)
65 , m_isWaitingForDidUpdate(false)
66 , m_isPaintingSuspended(!parameters.isVisible)
67 , m_alwaysUseCompositing(false)
68 , m_lastDisplayTime(0)
69 , m_displayTimer(WebProcess::shared().runLoop(), this, &DrawingAreaImpl::displayTimerFired)
70 , m_exitCompositingTimer(WebProcess::shared().runLoop(), this, &DrawingAreaImpl::exitAcceleratedCompositingMode)
71 {
72 if (webPage->corePage()->settings()->acceleratedDrawingEnabled())
73 m_alwaysUseCompositing = true;
74
75 if (m_alwaysUseCompositing)
76 enterAcceleratedCompositingMode(0);
77 }
78
setNeedsDisplay(const IntRect & rect)79 void DrawingAreaImpl::setNeedsDisplay(const IntRect& rect)
80 {
81 IntRect dirtyRect = rect;
82 dirtyRect.intersect(m_webPage->bounds());
83
84 if (dirtyRect.isEmpty())
85 return;
86
87 if (m_layerTreeHost) {
88 ASSERT(m_dirtyRegion.isEmpty());
89
90 m_layerTreeHost->setNonCompositedContentsNeedDisplay(dirtyRect);
91 return;
92 }
93
94 if (m_webPage->mainFrameHasCustomRepresentation())
95 return;
96
97 m_dirtyRegion.unite(dirtyRect);
98 scheduleDisplay();
99 }
100
scroll(const IntRect & scrollRect,const IntSize & scrollOffset)101 void DrawingAreaImpl::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
102 {
103 if (m_layerTreeHost) {
104 ASSERT(m_scrollRect.isEmpty());
105 ASSERT(m_scrollOffset.isEmpty());
106 ASSERT(m_dirtyRegion.isEmpty());
107
108 m_layerTreeHost->scrollNonCompositedContents(scrollRect, scrollOffset);
109 return;
110 }
111
112 if (m_webPage->mainFrameHasCustomRepresentation())
113 return;
114
115 if (!m_scrollRect.isEmpty() && scrollRect != m_scrollRect) {
116 unsigned scrollArea = scrollRect.width() * scrollRect.height();
117 unsigned currentScrollArea = m_scrollRect.width() * m_scrollRect.height();
118
119 if (currentScrollArea >= scrollArea) {
120 // The rect being scrolled is at least as large as the rect we'd like to scroll.
121 // Go ahead and just invalidate the scroll rect.
122 setNeedsDisplay(scrollRect);
123 return;
124 }
125
126 // Just repaint the entire current scroll rect, we'll scroll the new rect instead.
127 setNeedsDisplay(m_scrollRect);
128 m_scrollRect = IntRect();
129 m_scrollOffset = IntSize();
130 }
131
132 // Get the part of the dirty region that is in the scroll rect.
133 Region dirtyRegionInScrollRect = intersect(scrollRect, m_dirtyRegion);
134 if (!dirtyRegionInScrollRect.isEmpty()) {
135 // There are parts of the dirty region that are inside the scroll rect.
136 // We need to subtract them from the region, move them and re-add them.
137 m_dirtyRegion.subtract(scrollRect);
138
139 // Move the dirty parts.
140 Region movedDirtyRegionInScrollRect = intersect(translate(dirtyRegionInScrollRect, scrollOffset), scrollRect);
141
142 // And add them back.
143 m_dirtyRegion.unite(movedDirtyRegionInScrollRect);
144 }
145
146 // Compute the scroll repaint region.
147 Region scrollRepaintRegion = subtract(scrollRect, translate(scrollRect, scrollOffset));
148
149 m_dirtyRegion.unite(scrollRepaintRegion);
150
151 m_scrollRect = scrollRect;
152 m_scrollOffset += scrollOffset;
153 }
154
forceRepaint()155 void DrawingAreaImpl::forceRepaint()
156 {
157 setNeedsDisplay(m_webPage->bounds());
158
159 m_webPage->layoutIfNeeded();
160
161 if (m_layerTreeHost) {
162 m_layerTreeHost->forceRepaint();
163 if (!m_layerTreeHost->participatesInDisplay())
164 return;
165 }
166
167 m_isWaitingForDidUpdate = false;
168 display();
169 }
170
didInstallPageOverlay()171 void DrawingAreaImpl::didInstallPageOverlay()
172 {
173 if (m_layerTreeHost)
174 m_layerTreeHost->didInstallPageOverlay();
175 }
176
didUninstallPageOverlay()177 void DrawingAreaImpl::didUninstallPageOverlay()
178 {
179 if (m_layerTreeHost)
180 m_layerTreeHost->didUninstallPageOverlay();
181
182 setNeedsDisplay(m_webPage->bounds());
183 }
184
setPageOverlayNeedsDisplay(const IntRect & rect)185 void DrawingAreaImpl::setPageOverlayNeedsDisplay(const IntRect& rect)
186 {
187 if (m_layerTreeHost) {
188 m_layerTreeHost->setPageOverlayNeedsDisplay(rect);
189 return;
190 }
191
192 setNeedsDisplay(rect);
193 }
194
setLayerHostNeedsDisplay()195 void DrawingAreaImpl::setLayerHostNeedsDisplay()
196 {
197 ASSERT(m_layerTreeHost);
198 ASSERT(m_layerTreeHost->participatesInDisplay());
199 scheduleDisplay();
200 }
201
layerHostDidFlushLayers()202 void DrawingAreaImpl::layerHostDidFlushLayers()
203 {
204 ASSERT(m_layerTreeHost);
205
206 m_layerTreeHost->forceRepaint();
207
208 if (m_shouldSendDidUpdateBackingStoreState) {
209 sendDidUpdateBackingStoreState();
210 return;
211 }
212
213 if (!m_layerTreeHost || m_layerTreeHost->participatesInDisplay()) {
214 // When the layer tree host participates in display, we never tell the UI process about
215 // accelerated compositing. From the UI process's point of view, we're still just sending
216 // it a series of bitmaps in Update messages.
217 return;
218 }
219
220 #if USE(ACCELERATED_COMPOSITING)
221 m_webPage->send(Messages::DrawingAreaProxy::EnterAcceleratedCompositingMode(m_backingStoreStateID, m_layerTreeHost->layerTreeContext()));
222 #endif
223 }
224
setRootCompositingLayer(GraphicsLayer * graphicsLayer)225 void DrawingAreaImpl::setRootCompositingLayer(GraphicsLayer* graphicsLayer)
226 {
227 if (graphicsLayer) {
228 if (!m_layerTreeHost) {
229 // We're actually entering accelerated compositing mode.
230 enterAcceleratedCompositingMode(graphicsLayer);
231 } else {
232 m_exitCompositingTimer.stop();
233 // We're already in accelerated compositing mode, but the root compositing layer changed.
234 m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
235 }
236 } else {
237 if (m_layerTreeHost) {
238 m_layerTreeHost->setRootCompositingLayer(0);
239 if (!m_alwaysUseCompositing) {
240 // We'll exit accelerated compositing mode on a timer, to avoid re-entering
241 // compositing code via display() and layout.
242 // If we're leaving compositing mode because of a setSize, it is safe to
243 // exit accelerated compositing mode right away.
244 if (m_inUpdateBackingStoreState)
245 exitAcceleratedCompositingMode();
246 else
247 exitAcceleratedCompositingModeSoon();
248 }
249 }
250 }
251 }
252
scheduleCompositingLayerSync()253 void DrawingAreaImpl::scheduleCompositingLayerSync()
254 {
255 if (!m_layerTreeHost)
256 return;
257 m_layerTreeHost->scheduleLayerFlush();
258 }
259
syncCompositingLayers()260 void DrawingAreaImpl::syncCompositingLayers()
261 {
262 }
263
didReceiveMessage(CoreIPC::Connection *,CoreIPC::MessageID,CoreIPC::ArgumentDecoder *)264 void DrawingAreaImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
265 {
266 }
267
updateBackingStoreState(uint64_t stateID,bool respondImmediately,const WebCore::IntSize & size,const WebCore::IntSize & scrollOffset)268 void DrawingAreaImpl::updateBackingStoreState(uint64_t stateID, bool respondImmediately, const WebCore::IntSize& size, const WebCore::IntSize& scrollOffset)
269 {
270 ASSERT(!m_inUpdateBackingStoreState);
271 m_inUpdateBackingStoreState = true;
272
273 ASSERT_ARG(stateID, stateID >= m_backingStoreStateID);
274 if (stateID != m_backingStoreStateID) {
275 m_backingStoreStateID = stateID;
276 m_shouldSendDidUpdateBackingStoreState = true;
277
278 m_webPage->setSize(size);
279 m_webPage->layoutIfNeeded();
280 m_webPage->scrollMainFrameIfNotAtMaxScrollPosition(scrollOffset);
281
282 if (m_layerTreeHost)
283 m_layerTreeHost->sizeDidChange(size);
284 else
285 m_dirtyRegion = m_webPage->bounds();
286 } else {
287 ASSERT(size == m_webPage->size());
288 if (!m_shouldSendDidUpdateBackingStoreState) {
289 // We've already sent a DidUpdateBackingStoreState message for this state. We have nothing more to do.
290 m_inUpdateBackingStoreState = false;
291 return;
292 }
293 }
294
295 // The UI process has updated to a new backing store state. Any Update messages we sent before
296 // this point will be ignored. We wait to set this to false until after updating the page's
297 // size so that any displays triggered by the relayout will be ignored. If we're supposed to
298 // respond to the UpdateBackingStoreState message immediately, we'll do a display anyway in
299 // sendDidUpdateBackingStoreState; otherwise we shouldn't do one right now.
300 m_isWaitingForDidUpdate = false;
301
302 if (respondImmediately)
303 sendDidUpdateBackingStoreState();
304
305 m_inUpdateBackingStoreState = false;
306 }
307
sendDidUpdateBackingStoreState()308 void DrawingAreaImpl::sendDidUpdateBackingStoreState()
309 {
310 ASSERT(!m_isWaitingForDidUpdate);
311 ASSERT(m_shouldSendDidUpdateBackingStoreState);
312
313 m_shouldSendDidUpdateBackingStoreState = false;
314
315 UpdateInfo updateInfo;
316
317 if (!m_isPaintingSuspended && (!m_layerTreeHost || m_layerTreeHost->participatesInDisplay()))
318 display(updateInfo);
319
320 #if USE(ACCELERATED_COMPOSITING)
321 LayerTreeContext layerTreeContext;
322
323 if (m_isPaintingSuspended || (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())) {
324 updateInfo.viewSize = m_webPage->size();
325
326 if (m_layerTreeHost) {
327 layerTreeContext = m_layerTreeHost->layerTreeContext();
328
329 // We don't want the layer tree host to notify after the next scheduled
330 // layer flush because that might end up sending an EnterAcceleratedCompositingMode
331 // message back to the UI process, but the updated layer tree context
332 // will be sent back in the DidUpdateBackingStoreState message.
333 m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(false);
334 m_layerTreeHost->forceRepaint();
335 }
336 }
337
338 m_webPage->send(Messages::DrawingAreaProxy::DidUpdateBackingStoreState(m_backingStoreStateID, updateInfo, layerTreeContext));
339 #endif
340 }
341
didUpdate()342 void DrawingAreaImpl::didUpdate()
343 {
344 // We might get didUpdate messages from the UI process even after we've
345 // entered accelerated compositing mode. Ignore them.
346 if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())
347 return;
348
349 m_isWaitingForDidUpdate = false;
350
351 // Display if needed. We call displayTimerFired here since it will throttle updates to 60fps.
352 displayTimerFired();
353 }
354
suspendPainting()355 void DrawingAreaImpl::suspendPainting()
356 {
357 ASSERT(!m_isPaintingSuspended);
358
359 if (m_layerTreeHost)
360 m_layerTreeHost->pauseRendering();
361
362 m_isPaintingSuspended = true;
363 m_displayTimer.stop();
364 }
365
resumePainting()366 void DrawingAreaImpl::resumePainting()
367 {
368 if (!m_isPaintingSuspended) {
369 // FIXME: We can get a call to resumePainting when painting is not suspended.
370 // This happens when sending a synchronous message to create a new page. See <rdar://problem/8976531>.
371 return;
372 }
373
374 if (m_layerTreeHost)
375 m_layerTreeHost->resumeRendering();
376
377 m_isPaintingSuspended = false;
378
379 // FIXME: We shouldn't always repaint everything here.
380 setNeedsDisplay(m_webPage->bounds());
381 }
382
enterAcceleratedCompositingMode(GraphicsLayer * graphicsLayer)383 void DrawingAreaImpl::enterAcceleratedCompositingMode(GraphicsLayer* graphicsLayer)
384 {
385 m_exitCompositingTimer.stop();
386
387 ASSERT(!m_layerTreeHost);
388
389 m_layerTreeHost = LayerTreeHost::create(m_webPage);
390 if (!m_inUpdateBackingStoreState)
391 m_layerTreeHost->setShouldNotifyAfterNextScheduledLayerFlush(true);
392
393 m_layerTreeHost->setRootCompositingLayer(graphicsLayer);
394
395 // Non-composited content will now be handled exclusively by the layer tree host.
396 m_dirtyRegion = Region();
397 m_scrollRect = IntRect();
398 m_scrollOffset = IntSize();
399
400 if (!m_layerTreeHost->participatesInDisplay()) {
401 m_displayTimer.stop();
402 m_isWaitingForDidUpdate = false;
403 }
404 }
405
exitAcceleratedCompositingMode()406 void DrawingAreaImpl::exitAcceleratedCompositingMode()
407 {
408 if (m_alwaysUseCompositing)
409 return;
410
411 m_exitCompositingTimer.stop();
412
413 ASSERT(m_layerTreeHost);
414
415 bool wasParticipatingInDisplay = m_layerTreeHost->participatesInDisplay();
416
417 m_layerTreeHost->invalidate();
418 m_layerTreeHost = nullptr;
419 m_dirtyRegion = m_webPage->bounds();
420
421 if (m_inUpdateBackingStoreState)
422 return;
423
424 if (m_shouldSendDidUpdateBackingStoreState) {
425 sendDidUpdateBackingStoreState();
426 return;
427 }
428
429 UpdateInfo updateInfo;
430 if (m_isPaintingSuspended)
431 updateInfo.viewSize = m_webPage->size();
432 else
433 display(updateInfo);
434
435 #if USE(ACCELERATED_COMPOSITING)
436 if (wasParticipatingInDisplay) {
437 // When the layer tree host participates in display, we never tell the UI process about
438 // accelerated compositing. From the UI process's point of view, we're still just sending
439 // it a series of bitmaps in Update messages.
440 m_webPage->send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
441 } else {
442 // Send along a complete update of the page so we can paint the contents right after we exit the
443 // accelerated compositing mode, eliminiating flicker.
444 m_webPage->send(Messages::DrawingAreaProxy::ExitAcceleratedCompositingMode(m_backingStoreStateID, updateInfo));
445 }
446 #endif
447 }
448
exitAcceleratedCompositingModeSoon()449 void DrawingAreaImpl::exitAcceleratedCompositingModeSoon()
450 {
451 if (m_exitCompositingTimer.isActive())
452 return;
453
454 m_exitCompositingTimer.startOneShot(0);
455 }
456
scheduleDisplay()457 void DrawingAreaImpl::scheduleDisplay()
458 {
459 ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
460
461 if (m_isWaitingForDidUpdate)
462 return;
463
464 if (m_isPaintingSuspended)
465 return;
466
467 if (m_layerTreeHost) {
468 if (!m_layerTreeHost->needsDisplay())
469 return;
470 } else if (m_dirtyRegion.isEmpty())
471 return;
472
473 if (m_displayTimer.isActive())
474 return;
475
476 m_displayTimer.startOneShot(0);
477 }
478
displayTimerFired()479 void DrawingAreaImpl::displayTimerFired()
480 {
481 static const double minimumFrameInterval = 1.0 / 60.0;
482
483 double timeSinceLastDisplay = currentTime() - m_lastDisplayTime;
484 double timeUntilLayerTreeHostNeedsDisplay = m_layerTreeHost && m_layerTreeHost->participatesInDisplay() ? m_layerTreeHost->timeUntilNextDisplay() : 0;
485 double timeUntilNextDisplay = max(minimumFrameInterval - timeSinceLastDisplay, timeUntilLayerTreeHostNeedsDisplay);
486
487 if (timeUntilNextDisplay > 0) {
488 m_displayTimer.startOneShot(timeUntilNextDisplay);
489 return;
490 }
491
492 display();
493 }
494
display()495 void DrawingAreaImpl::display()
496 {
497 ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
498 ASSERT(!m_isWaitingForDidUpdate);
499 ASSERT(!m_inUpdateBackingStoreState);
500
501 if (m_isPaintingSuspended)
502 return;
503
504 if (m_layerTreeHost) {
505 if (!m_layerTreeHost->needsDisplay())
506 return;
507 } else if (m_dirtyRegion.isEmpty())
508 return;
509
510 if (m_shouldSendDidUpdateBackingStoreState) {
511 sendDidUpdateBackingStoreState();
512 return;
513 }
514
515 UpdateInfo updateInfo;
516 display(updateInfo);
517
518 if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay()) {
519 // The call to update caused layout which turned on accelerated compositing.
520 // Don't send an Update message in this case.
521 return;
522 }
523
524 m_webPage->send(Messages::DrawingAreaProxy::Update(m_backingStoreStateID, updateInfo));
525 m_isWaitingForDidUpdate = true;
526 }
527
shouldPaintBoundsRect(const IntRect & bounds,const Vector<IntRect> & rects)528 static bool shouldPaintBoundsRect(const IntRect& bounds, const Vector<IntRect>& rects)
529 {
530 const size_t rectThreshold = 10;
531 const float wastedSpaceThreshold = 0.75f;
532
533 if (rects.size() <= 1 || rects.size() > rectThreshold)
534 return true;
535
536 // Attempt to guess whether or not we should use the region bounds rect or the individual rects.
537 // We do this by computing the percentage of "wasted space" in the bounds. If that wasted space
538 // is too large, then we will do individual rect painting instead.
539 unsigned boundsArea = bounds.width() * bounds.height();
540 unsigned rectsArea = 0;
541 for (size_t i = 0; i < rects.size(); ++i)
542 rectsArea += rects[i].width() * rects[i].height();
543
544 float wastedSpace = 1 - (rectsArea / boundsArea);
545
546 return wastedSpace <= wastedSpaceThreshold;
547 }
548
display(UpdateInfo & updateInfo)549 void DrawingAreaImpl::display(UpdateInfo& updateInfo)
550 {
551 ASSERT(!m_isPaintingSuspended);
552 ASSERT(!m_layerTreeHost || m_layerTreeHost->participatesInDisplay());
553 ASSERT(!m_webPage->size().isEmpty());
554
555 // FIXME: It would be better if we could avoid painting altogether when there is a custom representation.
556 if (m_webPage->mainFrameHasCustomRepresentation()) {
557 // ASSUMPTION: the custom representation will be painting the dirty region for us.
558 m_dirtyRegion = Region();
559 return;
560 }
561
562 m_webPage->layoutIfNeeded();
563
564 // The layout may have put the page into accelerated compositing mode. If the LayerTreeHost is
565 // in charge of displaying, we have nothing more to do.
566 if (m_layerTreeHost && !m_layerTreeHost->participatesInDisplay())
567 return;
568
569 updateInfo.viewSize = m_webPage->size();
570
571 if (m_layerTreeHost)
572 m_layerTreeHost->display(updateInfo);
573 else {
574 IntRect bounds = m_dirtyRegion.bounds();
575 ASSERT(m_webPage->bounds().contains(bounds));
576
577 RefPtr<ShareableBitmap> bitmap = ShareableBitmap::createShareable(bounds.size(), ShareableBitmap::SupportsAlpha);
578 if (!bitmap->createHandle(updateInfo.bitmapHandle))
579 return;
580
581 Vector<IntRect> rects = m_dirtyRegion.rects();
582
583 if (shouldPaintBoundsRect(bounds, rects)) {
584 rects.clear();
585 rects.append(bounds);
586 }
587
588 updateInfo.scrollRect = m_scrollRect;
589 updateInfo.scrollOffset = m_scrollOffset;
590
591 m_dirtyRegion = Region();
592 m_scrollRect = IntRect();
593 m_scrollOffset = IntSize();
594
595 OwnPtr<GraphicsContext> graphicsContext = bitmap->createGraphicsContext();
596
597 updateInfo.updateRectBounds = bounds;
598
599 graphicsContext->translate(-bounds.x(), -bounds.y());
600
601 for (size_t i = 0; i < rects.size(); ++i) {
602 m_webPage->drawRect(*graphicsContext, rects[i]);
603 if (m_webPage->hasPageOverlay())
604 m_webPage->drawPageOverlay(*graphicsContext, rects[i]);
605 updateInfo.updateRects.append(rects[i]);
606 }
607 }
608
609 // Layout can trigger more calls to setNeedsDisplay and we don't want to process them
610 // until the UI process has painted the update, so we stop the timer here.
611 m_displayTimer.stop();
612
613 m_lastDisplayTime = currentTime();
614 }
615
616
617 } // namespace WebKit
618