[BlackBerry] Syncing up left over bits in Api from our local branch to upstream
[WebKit-https.git] / Source / WebKit / blackberry / Api / BackingStore.cpp
1 /*
2  * Copyright (C) 2009, 2010, 2011 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "BackingStore.h"
21
22 #include "BackingStoreClient.h"
23 #include "BackingStoreCompositingSurface.h"
24 #include "BackingStoreTile.h"
25 #include "BackingStore_p.h"
26 #include "FatFingers.h"
27 #include "Frame.h"
28 #include "FrameView.h"
29 #include "GraphicsContext.h"
30 #include "InspectorController.h"
31 #include "Page.h"
32 #include "SurfacePool.h"
33 #include "WebPage.h"
34 #include "WebPageClient.h"
35 #include "WebPage_p.h"
36 #include "WebSettings.h"
37
38 #include <BlackBerryPlatformExecutableMessage.h>
39 #include <BlackBerryPlatformIntRectRegion.h>
40 #include <BlackBerryPlatformLog.h>
41 #include <BlackBerryPlatformMessage.h>
42 #include <BlackBerryPlatformMessageClient.h>
43 #include <BlackBerryPlatformWindow.h>
44
45 #include <wtf/CurrentTime.h>
46 #include <wtf/MathExtras.h>
47 #include <wtf/NotFound.h>
48
49 #define SUPPRESS_NON_VISIBLE_REGULAR_RENDER_JOBS 0
50 #define ENABLE_SCROLLBARS 1
51 #define ENABLE_REPAINTONSCROLL 1
52 #define DEBUG_BACKINGSTORE 0
53 #define DEBUG_CHECKERBOARD 0
54 #define DEBUG_WEBCORE_REQUESTS 0
55 #define DEBUG_VISUALIZE 0
56 #define DEBUG_TILEMATRIX 0
57 #define DEBUG_COMPOSITING_DIRTY_REGION 0
58
59 #include <BlackBerryPlatformScreen.h>
60
61 using namespace WebCore;
62 using namespace std;
63
64 using BlackBerry::Platform::Graphics::Window;
65 using BlackBerry::Platform::IntRect;
66 using BlackBerry::Platform::IntPoint;
67 using BlackBerry::Platform::IntSize;
68
69 namespace BlackBerry {
70 namespace WebKit {
71
72 const int s_renderTimerTimeout = 1.0;
73 WebPage* BackingStorePrivate::s_currentBackingStoreOwner = 0;
74
75 typedef std::pair<int, int> Divisor;
76 typedef Vector<Divisor> DivisorList;
77 // FIXME: Cache this and/or use a smarter algorithm.
78 static DivisorList divisors(unsigned n)
79 {
80     DivisorList divisors;
81     for (unsigned i = 1; i <= n; ++i)
82         if (!(n % i))
83             divisors.append(std::make_pair(i, n / i));
84     return divisors;
85 }
86
87 static bool divisorIsPerfectWidth(Divisor divisor, Platform::IntSize size, int tileWidth)
88 {
89     return size.width() <= divisor.first * tileWidth && abs(size.width() - divisor.first * tileWidth) < tileWidth;
90 }
91
92 static bool divisorIsPerfectHeight(Divisor divisor, Platform::IntSize size, int tileHeight)
93 {
94     return size.height() <= divisor.second * tileHeight && abs(size.height() - divisor.second * tileHeight) < tileHeight;
95 }
96
97 static bool divisorIsPreferredDirection(Divisor divisor, BackingStorePrivate::TileMatrixDirection direction)
98 {
99     if (direction == BackingStorePrivate::Vertical)
100         return divisor.second > divisor.first;
101     return divisor.first > divisor.second;
102 }
103
104 // Compute best divisor given the ratio determined by size.
105 static Divisor bestDivisor(Platform::IntSize size, int tileWidth, int tileHeight,
106                            int minimumNumberOfTilesWide, int minimumNumberOfTilesHigh,
107                            BackingStorePrivate::TileMatrixDirection direction)
108 {
109     // The point of this function is to determine the number of tiles in each
110     // dimension. We do this by looking to match the tile matrix width/height
111     // ratio as closely as possible with the width/height ratio of the contents.
112     // We also look at the direction passed to give preference to one dimension
113     // over another. This method could probably be made faster, but it gets the
114     // job done.
115     SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
116     ASSERT(!surfacePool->isEmpty());
117
118     // Store a static list of possible divisors.
119     static DivisorList divisorList = divisors(surfacePool->size());
120
121     // The ratio we're looking to best imitate.
122     float ratio = static_cast<float>(size.width()) / static_cast<float>(size.height());
123
124     Divisor bestDivisor;
125     for (size_t i = 0; i < divisorList.size(); ++i) {
126         Divisor divisor = divisorList[i];
127
128         const bool isPerfectWidth = divisorIsPerfectWidth(divisor, size, tileWidth);
129         const bool isPerfectHeight = divisorIsPerfectHeight(divisor, size, tileHeight);
130         const bool isValidWidth = divisor.first >= minimumNumberOfTilesWide || isPerfectWidth;
131         const bool isValidHeight = divisor.second >= minimumNumberOfTilesHigh || isPerfectHeight;
132         if (!isValidWidth || !isValidHeight)
133             continue;
134
135         if (isPerfectWidth || isPerfectHeight) {
136             bestDivisor = divisor; // Found a perfect fit!
137 #if DEBUG_TILEMATRIX
138             BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "bestDivisor found perfect size isPerfectWidth=%s isPerfectHeight=%s",
139                                    isPerfectWidth ? "true" : "false",
140                                    isPerfectHeight ? "true" : "false");
141 #endif
142             break;
143         }
144
145         // Store basis of comparison.
146         if (!bestDivisor.first || !bestDivisor.second) {
147             bestDivisor = divisor;
148             continue;
149         }
150
151         // If the current best divisor agrees with the preferred tile matrix direction,
152         // then continue if the current candidate does not.
153         if (divisorIsPreferredDirection(bestDivisor, direction) && !divisorIsPreferredDirection(divisor, direction))
154             continue;
155
156         // Compare ratios.
157         float diff1 = fabs((static_cast<float>(divisor.first) / static_cast<float>(divisor.second)) - ratio);
158         float diff2 = fabs((static_cast<float>(bestDivisor.first) / static_cast<float>(bestDivisor.second)) - ratio);
159         if (diff1 < diff2)
160             bestDivisor = divisor;
161     }
162
163     return bestDivisor;
164 }
165
166 struct BackingStoreMutexLocker {
167     BackingStoreMutexLocker(BackingStorePrivate* backingStorePrivate)
168         : m_backingStorePrivate(backingStorePrivate)
169     {
170         m_backingStorePrivate->lockBackingStore();
171     }
172
173     ~BackingStoreMutexLocker()
174     {
175         m_backingStorePrivate->unlockBackingStore();
176     }
177
178 private:
179     BackingStorePrivate* m_backingStorePrivate;
180 };
181
182 Platform::IntRect BackingStoreGeometry::backingStoreRect() const
183 {
184     return Platform::IntRect(backingStoreOffset(), backingStoreSize());
185 }
186
187 Platform::IntSize BackingStoreGeometry::backingStoreSize() const
188 {
189     return Platform::IntSize(numberOfTilesWide() * BackingStorePrivate::tileWidth(), numberOfTilesHigh() * BackingStorePrivate::tileHeight());
190 }
191
192 BackingStorePrivate::BackingStorePrivate()
193     : m_suspendScreenUpdates(false)
194     , m_suspendBackingStoreUpdates(false)
195     , m_suspendRenderJobs(false)
196     , m_suspendRegularRenderJobs(false)
197     , m_isScrollingOrZooming(false)
198     , m_webPage(0)
199     , m_client(0)
200     , m_renderQueue(adoptPtr(new RenderQueue(this)))
201     , m_defersBlit(true)
202     , m_hasBlitJobs(false)
203     , m_currentWindowBackBuffer(0)
204     , m_preferredTileMatrixDimension(Vertical)
205     , m_blitGeneration(-1)
206 #if USE(ACCELERATED_COMPOSITING)
207     , m_needsDrawLayersOnCommit(false)
208     , m_isDirectRenderingAnimationMessageScheduled(false)
209 #endif
210 {
211     m_frontState = reinterpret_cast<unsigned>(new BackingStoreGeometry);
212     m_backState = reinterpret_cast<unsigned>(new BackingStoreGeometry);
213
214     m_renderTimer = adoptPtr(new Timer<BackingStorePrivate>(this, &BackingStorePrivate::renderOnTimer));
215
216     // Need a recursive mutex to achieve a global lock.
217     pthread_mutexattr_t attr;
218     pthread_mutexattr_init(&attr);
219     pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
220     pthread_mutex_init(&m_mutex, &attr);
221     pthread_mutexattr_destroy(&attr);
222
223     pthread_mutex_init(&m_blitGenerationLock, 0);
224     pthread_cond_init(&m_blitGenerationCond, 0);
225 }
226
227 BackingStorePrivate::~BackingStorePrivate()
228 {
229     BackingStoreGeometry* front = reinterpret_cast<BackingStoreGeometry*>(m_frontState);
230     delete front;
231     m_frontState = 0;
232
233     BackingStoreGeometry* back = reinterpret_cast<BackingStoreGeometry*>(m_backState);
234     delete back;
235     m_backState = 0;
236
237     pthread_cond_destroy(&m_blitGenerationCond);
238     pthread_mutex_destroy(&m_blitGenerationLock);
239     pthread_mutex_destroy(&m_mutex);
240 }
241
242 bool BackingStorePrivate::shouldDirectRenderingToWindow() const
243 {
244     if (m_webPage->settings()->isDirectRenderingToWindowEnabled() || !isActive())
245         return true;
246
247     const BackingStoreGeometry* currentState = frontState();
248     const unsigned tilesNecessary = minimumNumberOfTilesWide() * minimumNumberOfTilesHigh();
249     const unsigned tilesAvailable = currentState->numberOfTilesWide() * currentState->numberOfTilesHigh();
250     return tilesAvailable < tilesNecessary;
251 }
252
253 bool BackingStorePrivate::isOpenGLCompositing() const
254 {
255     if (Window* window = m_webPage->client()->window())
256         return window->windowUsage() == Window::GLES2Usage;
257
258     // If there's no window, OpenGL rendering is currently the only option.
259     return true;
260 }
261
262 void BackingStorePrivate::suspendScreenAndBackingStoreUpdates()
263 {
264     m_suspendBackingStoreUpdates = true;
265
266     // Make sure the user interface thread gets the message before we proceed
267     // because blitContents can be called from this thread and it must honor
268     // this flag.
269     m_suspendScreenUpdates = true;
270     BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
271
272 #if USE(ACCELERATED_COMPOSITING)
273     m_webPage->d->resetCompositingSurface();
274 #endif
275 }
276
277 void BackingStorePrivate::resumeScreenAndBackingStoreUpdates(BackingStore::ResumeUpdateOperation op)
278 {
279     m_suspendBackingStoreUpdates = false;
280
281 #if USE(ACCELERATED_COMPOSITING)
282     if (op != BackingStore::None)
283         m_webPage->d->setNeedsOneShotDrawingSynchronization();
284 #endif
285
286     // For the direct rendering case, there is no such operation as blit,
287     // we have to render to get anything to the screen.
288     if (shouldDirectRenderingToWindow() && op == BackingStore::Blit)
289         op = BackingStore::RenderAndBlit;
290
291     // Do some rendering if necessary.
292     if (op == BackingStore::RenderAndBlit)
293         renderVisibleContents();
294
295     // Make sure the user interface thread gets the message before we proceed
296     // because blitContents can be called from the user interface thread and
297     // it must honor this flag.
298     m_suspendScreenUpdates = false;
299     BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
300
301     // Do some blitting if necessary.
302     if ((op == BackingStore::Blit || op == BackingStore::RenderAndBlit) && !shouldDirectRenderingToWindow())
303         blitVisibleContents();
304 }
305
306 void BackingStorePrivate::repaint(const Platform::IntRect& windowRect,
307                                   bool contentChanged, bool immediate)
308 {
309     if (m_suspendBackingStoreUpdates)
310         return;
311
312      // If immediate is true, then we're being asked to perform synchronously.
313      // NOTE: WebCore::ScrollView will call this method with immediate:true and contentChanged:false.
314      // This is a special case introduced specifically for the Apple's windows port and can be safely ignored I believe.
315      // Now this method will be called from WebPagePrivate::repaint().
316
317     if (contentChanged && !windowRect.isEmpty()) {
318         // This windowRect is in untransformed coordinates relative to the viewport, but
319         // it needs to be transformed coordinates relative to the transformed contents.
320         Platform::IntRect rect = m_webPage->d->mapToTransformed(m_client->mapFromViewportToContents(windowRect));
321         rect.inflate(1 /*dx*/, 1 /*dy*/); // Account for anti-aliasing of previous rendering runs.
322
323         // FIXME: This should not explicitely depend on WebCore::.
324         WebCore::IntRect tmpRect = rect;
325         m_client->clipToTransformedContentsRect(tmpRect);
326
327         rect = tmpRect;
328         if (rect.isEmpty())
329             return;
330
331 #if DEBUG_WEBCORE_REQUESTS
332         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
333                                   "BackingStorePrivate::repaint rect=%d,%d %dx%d contentChanged=%s immediate=%s",
334                                   rect.x(), rect.y(), rect.width(), rect.height(),
335                                   (contentChanged ? "true" : "false"),
336                                   (immediate ? "true" : "false"));
337 #endif
338
339         if (immediate) {
340             if (render(rect) && !shouldDirectRenderingToWindow())
341                 blitVisibleContents();
342         } else
343             m_renderQueue->addToQueue(RenderQueue::RegularRender, rect);
344     }
345 }
346
347 void BackingStorePrivate::slowScroll(const Platform::IntSize& delta, const Platform::IntRect& windowRect, bool immediate)
348 {
349 #if DEBUG_BACKINGSTORE
350     // Start the time measurement...
351     double time = WTF::currentTime();
352 #endif
353
354     scrollingStartedHelper(delta);
355
356     // This windowRect is in untransformed coordinates relative to the viewport, but
357     // it needs to be transformed coordinates relative to the transformed contents.
358     Platform::IntRect rect = m_webPage->d->mapToTransformed(m_client->mapFromViewportToContents(windowRect));
359
360     if (immediate) {
361         if (render(rect) && !isSuspended() && !shouldDirectRenderingToWindow())
362             blitVisibleContents();
363     } else {
364         m_renderQueue->addToQueue(RenderQueue::VisibleScroll, rect);
365         // We only blit here if the client did not generate the scroll as the client
366         // now supports blitting asynchronously during scroll operations.
367         if (!m_client->isClientGeneratedScroll() && !shouldDirectRenderingToWindow())
368             blitVisibleContents();
369     }
370
371 #if DEBUG_BACKINGSTORE
372     // Stop the time measurement.
373     double elapsed = WTF::currentTime() - time;
374     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::slowScroll elapsed=%f", elapsed);
375 #endif
376 }
377
378 void BackingStorePrivate::scroll(const Platform::IntSize& delta,
379                                  const Platform::IntRect& scrollViewRect,
380                                  const Platform::IntRect& clipRect)
381 {
382     // If we are direct rendering then we are forced to go down the slow path
383     // to scrolling.
384     if (shouldDirectRenderingToWindow()) {
385         Platform::IntRect viewportRect(Platform::IntPoint(0, 0), m_webPage->d->transformedViewportSize());
386         slowScroll(delta, m_webPage->d->mapFromTransformed(viewportRect), true /*immediate*/);
387         return;
388     }
389
390 #if DEBUG_BACKINGSTORE
391     // Start the time measurement...
392     double time = WTF::currentTime();
393 #endif
394
395     scrollingStartedHelper(delta);
396
397     // We only blit here if the client did not generate the scroll as the client
398     // now supports blitting asynchronously during scroll operations.
399     if (!m_client->isClientGeneratedScroll())
400         blitVisibleContents();
401
402 #if DEBUG_BACKINGSTORE
403     // Stop the time measurement.
404     double elapsed = WTF::currentTime() - time;
405     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::scroll dx=%d, dy=%d elapsed=%f", delta.width(), delta.height(), elapsed);
406 #endif
407 }
408
409 void BackingStorePrivate::scrollingStartedHelper(const Platform::IntSize& delta)
410 {
411     // Notify the render queue so that it can shuffle accordingly.
412     m_renderQueue->updateSortDirection(delta.width(), delta.height());
413     m_renderQueue->visibleContentChanged(visibleContentsRect());
414
415     // Scroll the actual backingstore.
416     scrollBackingStore(delta.width(), delta.height());
417
418     // Add any newly visible tiles that have not been previously rendered to the queue
419     // and check if the tile was previously rendered by regular render job.
420     updateTilesForScrollOrNotRenderedRegion();
421 }
422
423 bool BackingStorePrivate::shouldSuppressNonVisibleRegularRenderJobs() const
424 {
425 #if SUPPRESS_NON_VISIBLE_REGULAR_RENDER_JOBS
426     return true;
427 #else
428     // Always suppress when loading as this drastically decreases page loading
429     // time...
430     return m_client->isLoading();
431 #endif
432 }
433
434 bool BackingStorePrivate::shouldPerformRenderJobs() const
435 {
436     return (m_webPage->isVisible() || shouldDirectRenderingToWindow()) && !m_suspendRenderJobs && !m_suspendBackingStoreUpdates && !m_renderQueue->isEmpty(!m_suspendRegularRenderJobs);
437 }
438
439 bool BackingStorePrivate::shouldPerformRegularRenderJobs() const
440 {
441     return shouldPerformRenderJobs() && !m_suspendRegularRenderJobs;
442 }
443
444 void BackingStorePrivate::startRenderTimer()
445 {
446     // Called when render queue has a new job added.
447     if (m_renderTimer->isActive() || m_renderQueue->isEmpty(!m_suspendRegularRenderJobs))
448         return;
449
450 #if DEBUG_BACKINGSTORE
451     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::startRenderTimer time=%f", WTF::currentTime());
452 #endif
453     m_renderTimer->startOneShot(s_renderTimerTimeout);
454 }
455
456 void BackingStorePrivate::stopRenderTimer()
457 {
458     if (!m_renderTimer->isActive())
459         return;
460
461     // Called when we render something to restart.
462 #if DEBUG_BACKINGSTORE
463     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::stopRenderTimer time=%f", WTF::currentTime());
464 #endif
465     m_renderTimer->stop();
466 }
467
468 void BackingStorePrivate::renderOnTimer(WebCore::Timer<BackingStorePrivate>*)
469 {
470     // This timer is a third method of starting a render operation that is a catch-all. If more
471     // than s_renderTimerTimeout elapses with no rendering taking place and render jobs in the queue, then
472     // renderOnTimer will be called which will actually render.
473     if (!shouldPerformRenderJobs())
474         return;
475
476 #if DEBUG_BACKINGSTORE
477     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::renderOnTimer time=%f", WTF::currentTime());
478 #endif
479     while (m_renderQueue->hasCurrentVisibleZoomJob() || m_renderQueue->hasCurrentVisibleScrollJob())
480         m_renderQueue->render(!m_suspendRegularRenderJobs);
481
482     if (shouldPerformRegularRenderJobs() && m_renderQueue->hasCurrentRegularRenderJob())
483         m_renderQueue->renderAllCurrentRegularRenderJobs();
484
485 #if USE(ACCELERATED_COMPOSITING)
486     drawLayersOnCommitIfNeeded();
487 #endif
488 }
489
490 void BackingStorePrivate::renderOnIdle()
491 {
492     ASSERT(shouldPerformRenderJobs());
493
494     // Let the render queue know that we entered a new event queue cycle
495     // so it can determine if it is under pressure.
496     m_renderQueue->eventQueueCycled();
497
498 #if DEBUG_BACKINGSTORE
499     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::renderOnIdle");
500 #endif
501
502     m_renderQueue->render(!m_suspendRegularRenderJobs);
503
504 #if USE(ACCELERATED_COMPOSITING)
505     drawLayersOnCommitIfNeeded();
506 #endif
507 }
508
509 bool BackingStorePrivate::willFireTimer()
510 {
511     // Let the render queue know that we entered a new event queue cycle
512     // so it can determine if it is under pressure.
513     m_renderQueue->eventQueueCycled();
514
515     if (!shouldPerformRegularRenderJobs() || !m_renderQueue->hasCurrentRegularRenderJob() || !m_renderQueue->currentRegularRenderJobBatchUnderPressure())
516         return true;
517
518 #if DEBUG_BACKINGSTORE
519     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::willFireTimer");
520 #endif
521
522     // We've detected that the regular render jobs are coming under pressure likely
523     // due to timers firing producing invalidation jobs and our efforts to break them
524     // up into bite size pieces has produced a situation where we can not complete
525     // a batch of them before receiving more that intersect them which causes us
526     // to start the batch over. To mitigate this we have to empty the current batch
527     // when this is detected.
528
529     // We still want to perform priority jobs first to avoid redundant paints.
530     while (m_renderQueue->hasCurrentVisibleZoomJob() || m_renderQueue->hasCurrentVisibleScrollJob())
531         m_renderQueue->render(!m_suspendRegularRenderJobs);
532
533     if (m_renderQueue->hasCurrentRegularRenderJob())
534         m_renderQueue->renderAllCurrentRegularRenderJobs();
535
536 #if USE(ACCELERATED_COMPOSITING)
537     drawLayersOnCommitIfNeeded();
538 #endif
539
540     // Let the caller yield and reschedule the timer.
541     return false;
542 }
543
544 Platform::IntRect BackingStorePrivate::expandedContentsRect() const
545 {
546     return Platform::IntRect(Platform::IntPoint(0, 0), expandedContentsSize());
547 }
548
549 Platform::IntRect BackingStorePrivate::visibleContentsRect() const
550 {
551     return intersection(m_client->transformedVisibleContentsRect(),
552                         Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()));
553 }
554
555 Platform::IntRect BackingStorePrivate::unclippedVisibleContentsRect() const
556 {
557     return m_client->transformedVisibleContentsRect();
558 }
559
560 bool BackingStorePrivate::shouldMoveLeft(const Platform::IntRect& backingStoreRect) const
561 {
562     return canMoveX(backingStoreRect)
563             && backingStoreRect.x() > visibleContentsRect().x()
564             && backingStoreRect.x() > expandedContentsRect().x();
565 }
566
567 bool BackingStorePrivate::shouldMoveRight(const Platform::IntRect& backingStoreRect) const
568 {
569     return canMoveX(backingStoreRect)
570             && backingStoreRect.right() < visibleContentsRect().right()
571             && backingStoreRect.right() < expandedContentsRect().right();
572 }
573
574 bool BackingStorePrivate::shouldMoveUp(const Platform::IntRect& backingStoreRect) const
575 {
576     return canMoveY(backingStoreRect)
577             && backingStoreRect.y() > visibleContentsRect().y()
578             && backingStoreRect.y() > expandedContentsRect().y();
579 }
580
581 bool BackingStorePrivate::shouldMoveDown(const Platform::IntRect& backingStoreRect) const
582 {
583     return canMoveY(backingStoreRect)
584             && backingStoreRect.bottom() < visibleContentsRect().bottom()
585             && backingStoreRect.bottom() < expandedContentsRect().bottom();
586 }
587
588 bool BackingStorePrivate::canMoveX(const Platform::IntRect& backingStoreRect) const
589 {
590     return backingStoreRect.width() > visibleContentsRect().width();
591 }
592
593 bool BackingStorePrivate::canMoveY(const Platform::IntRect& backingStoreRect) const
594 {
595     return backingStoreRect.height() > visibleContentsRect().height();
596 }
597
598 bool BackingStorePrivate::canMoveLeft(const Platform::IntRect& rect) const
599 {
600     Platform::IntRect backingStoreRect = rect;
601     Platform::IntRect visibleContentsRect = this->visibleContentsRect();
602     Platform::IntRect contentsRect = this->expandedContentsRect();
603     backingStoreRect.move(-tileWidth(), 0);
604     return backingStoreRect.right() >= visibleContentsRect.right()
605             && backingStoreRect.x() >= contentsRect.x();
606 }
607
608 bool BackingStorePrivate::canMoveRight(const Platform::IntRect& rect) const
609 {
610     Platform::IntRect backingStoreRect = rect;
611     Platform::IntRect visibleContentsRect = this->visibleContentsRect();
612     Platform::IntRect contentsRect = this->expandedContentsRect();
613     backingStoreRect.move(tileWidth(), 0);
614     return backingStoreRect.x() <= visibleContentsRect.x()
615             && (backingStoreRect.right() <= contentsRect.right()
616             || (backingStoreRect.right() - contentsRect.right()) < tileWidth());
617 }
618
619 bool BackingStorePrivate::canMoveUp(const Platform::IntRect& rect) const
620 {
621     Platform::IntRect backingStoreRect = rect;
622     Platform::IntRect visibleContentsRect = this->visibleContentsRect();
623     Platform::IntRect contentsRect = this->expandedContentsRect();
624     backingStoreRect.move(0, -tileHeight());
625     return backingStoreRect.bottom() >= visibleContentsRect.bottom()
626             && backingStoreRect.y() >= contentsRect.y();
627 }
628
629 bool BackingStorePrivate::canMoveDown(const Platform::IntRect& rect) const
630 {
631     Platform::IntRect backingStoreRect = rect;
632     Platform::IntRect visibleContentsRect = this->visibleContentsRect();
633     Platform::IntRect contentsRect = this->expandedContentsRect();
634     backingStoreRect.move(0, tileHeight());
635     return backingStoreRect.y() <= visibleContentsRect.y()
636             && (backingStoreRect.bottom() <= contentsRect.bottom()
637             || (backingStoreRect.bottom() - contentsRect.bottom()) < tileHeight());
638 }
639
640 Platform::IntRect BackingStorePrivate::backingStoreRectForScroll(int deltaX, int deltaY, const Platform::IntRect& rect) const
641 {
642     // The current rect.
643     Platform::IntRect backingStoreRect = rect;
644
645     // This method uses the delta values to describe the backingstore rect
646     // given the current scroll direction and the viewport position. However,
647     // this method can be called with no deltas whatsoever for instance when
648     // the contents size changes or the orientation changes. In this case, we
649     // want to use the previous scroll direction to describe the backingstore
650     // rect. This will result in less checkerboard.
651     if (!deltaX && !deltaY) {
652         deltaX = m_previousDelta.width();
653         deltaY = m_previousDelta.height();
654     }
655     m_previousDelta = Platform::IntSize(deltaX, deltaY);
656
657     // Return to origin if need be.
658     if (!canMoveX(backingStoreRect) && backingStoreRect.x())
659         backingStoreRect.setX(0);
660
661     if (!canMoveY(backingStoreRect) && backingStoreRect.y())
662         backingStoreRect.setY(0);
663
664     // Move the rect left.
665     while (shouldMoveLeft(backingStoreRect) || (deltaX > 0 && canMoveLeft(backingStoreRect)))
666         backingStoreRect.move(-tileWidth(), 0);
667
668     // Move the rect right.
669     while (shouldMoveRight(backingStoreRect) || (deltaX < 0 && canMoveRight(backingStoreRect)))
670         backingStoreRect.move(tileWidth(), 0);
671
672     // Move the rect up.
673     while (shouldMoveUp(backingStoreRect) || (deltaY > 0 && canMoveUp(backingStoreRect)))
674         backingStoreRect.move(0, -tileHeight());
675
676     // Move the rect down.
677     while (shouldMoveDown(backingStoreRect) || (deltaY < 0 && canMoveDown(backingStoreRect)))
678         backingStoreRect.move(0, tileHeight());
679
680     return backingStoreRect;
681 }
682
683 void BackingStorePrivate::setBackingStoreRect(const Platform::IntRect& backingStoreRect)
684 {
685     if (!m_webPage->isVisible())
686         return;
687
688     if (!isActive()) {
689         m_webPage->d->setShouldResetTilesWhenShown(true);
690         return;
691     }
692
693     Platform::IntRect currentBackingStoreRect = frontState()->backingStoreRect();
694
695     if (backingStoreRect == currentBackingStoreRect)
696         return;
697
698 #if DEBUG_TILEMATRIX
699     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::setBackingStoreRect changed from (%d,%d %dx%d) to (%d,%d %dx%d)",
700                            currentBackingStoreRect.x(),
701                            currentBackingStoreRect.y(),
702                            currentBackingStoreRect.width(),
703                            currentBackingStoreRect.height(),
704                            backingStoreRect.x(),
705                            backingStoreRect.y(),
706                            backingStoreRect.width(),
707                            backingStoreRect.height());
708 #endif
709
710     BackingStoreGeometry* currentState = frontState();
711     TileMap currentMap = currentState->tileMap();
712
713     TileIndexList indexesToFill = indexesForBackingStoreRect(backingStoreRect);
714
715     ASSERT(static_cast<int>(indexesToFill.size()) == currentMap.size());
716
717     TileMap newTileMap;
718     TileMap leftOverTiles;
719
720     // Iterate through our current tile map and add tiles that are rendered with
721     // our new backing store rect.
722     TileMap::const_iterator tileMapEnd = currentMap.end();
723     for (TileMap::const_iterator it = currentMap.begin(); it != tileMapEnd; ++it) {
724         TileIndex oldIndex = it->first;
725         BackingStoreTile* tile = it->second;
726
727         // Reset the old index.
728         resetTile(oldIndex, tile, false /*resetBackground*/);
729
730         // Origin of last committed render for tile in transformed content coordinates.
731         Platform::IntPoint origin = originOfLastRenderForTile(oldIndex, tile, currentBackingStoreRect);
732
733         // If the new backing store rect contains this origin, then insert the tile there
734         // and mark it as no longer shifted. Note: Platform::IntRect::contains checks for a 1x1 rect
735         // below and to the right of the origin so it is correct usage here.
736         if (backingStoreRect.contains(origin)) {
737             TileIndex newIndex = indexOfTile(origin, backingStoreRect);
738             Platform::IntRect rect(origin, tileSize());
739             if (m_renderQueue->regularRenderJobsPreviouslyAttemptedButNotRendered(rect)) {
740                 // If the render queue previously tried to render this tile, but the
741                 // backingstore wasn't in the correct place or the tile wasn't visible
742                 // at the time then we can't simply restore the tile since the content
743                 // is now invalid as far as WebKit is concerned. Instead, we clear
744                 // the tile here of the region and then put the tile in the render
745                 // queue again.
746
747                 // Intersect the tile with the not rendered region to get the areas
748                 // of the tile that we need to clear.
749                 Platform::IntRectRegion tileNotRenderedRegion = Platform::IntRectRegion::intersectRegions(m_renderQueue->regularRenderJobsNotRenderedRegion(), rect);
750                 clearAndUpdateTileOfNotRenderedRegion(newIndex, tile, tileNotRenderedRegion, backingStoreRect);
751 #if DEBUG_BACKINGSTORE
752                 Platform::IntRect extents = tileNotRenderedRegion.extents();
753                 BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::setBackingStoreRect did clear tile %d,%d %dx%d",
754                                        extents.x(), extents.y(), extents.width(), extents.height());
755 #endif
756             } else {
757                 // Mark as needing update.
758                 if (!tile->frontBuffer()->isRendered()
759                     && !isCurrentVisibleJob(newIndex, tile, backingStoreRect))
760                     updateTile(origin, false /*immediate*/);
761             }
762
763             // Do some bookkeeping with shifting tiles...
764             tile->clearShift();
765             tile->setCommitted(true);
766
767             size_t i = indexesToFill.find(newIndex);
768             ASSERT(i != WTF::notFound);
769             indexesToFill.remove(i);
770             newTileMap.add(newIndex, tile);
771         } else {
772             // Store this tile and index so we can add it to the remaining left over spots...
773             leftOverTiles.add(oldIndex, tile);
774         }
775     }
776
777     ASSERT(static_cast<int>(indexesToFill.size()) == leftOverTiles.size());
778     size_t i = 0;
779     TileMap::const_iterator leftOverEnd = leftOverTiles.end();
780     for (TileMap::const_iterator it = leftOverTiles.begin(); it != leftOverEnd; ++it) {
781         TileIndex oldIndex = it->first;
782         BackingStoreTile* tile = it->second;
783         if (i >= indexesToFill.size()) {
784             ASSERT_NOT_REACHED();
785             break;
786         }
787
788         TileIndex newIndex = indexesToFill.at(i);
789
790         // Origin of last committed render for tile in transformed content coordinates.
791         Platform::IntPoint originOfOld = originOfLastRenderForTile(oldIndex, tile, currentBackingStoreRect);
792         // Origin of the new index for the new backing store rect.
793         Platform::IntPoint originOfNew = originOfTile(newIndex, backingStoreRect);
794
795         // Mark as needing update.
796         updateTile(originOfNew, false /*immediate*/);
797
798         tile->clearShift();
799         tile->setCommitted(false);
800         tile->setHorizontalShift((originOfOld.x() - originOfNew.x()) / tileWidth());
801         tile->setVerticalShift((originOfOld.y() - originOfNew.y()) / tileHeight());
802
803         newTileMap.add(newIndex, tile);
804
805         ++i;
806     }
807
808     // Checks to make sure we haven't lost any tiles.
809     ASSERT(currentMap.size() == newTileMap.size());
810
811     backState()->setNumberOfTilesWide(backingStoreRect.width() / tileWidth());
812     backState()->setNumberOfTilesHigh(backingStoreRect.height() / tileHeight());
813     backState()->setBackingStoreOffset(backingStoreRect.location());
814     backState()->setTileMap(newTileMap);
815
816     swapState();
817 }
818
819 BackingStorePrivate::TileIndexList BackingStorePrivate::indexesForBackingStoreRect(const Platform::IntRect& backingStoreRect) const
820 {
821     TileIndexList indexes;
822     int numberOfTilesWide = backingStoreRect.width() / tileWidth();
823     int numberOfTilesHigh = backingStoreRect.height() / tileHeight();
824     for (int y = 0; y < numberOfTilesHigh; ++y) {
825         for (int x = 0; x < numberOfTilesWide; ++x) {
826             TileIndex index(x, y);
827             indexes.append(index);
828         }
829     }
830     return indexes;
831 }
832
833 Platform::IntPoint BackingStorePrivate::originOfLastRenderForTile(const TileIndex& index,
834                                                                  BackingStoreTile* tile,
835                                                                  const Platform::IntRect& backingStoreRect) const
836 {
837     return originOfTile(indexOfLastRenderForTile(index, tile), backingStoreRect);
838 }
839
840 TileIndex BackingStorePrivate::indexOfLastRenderForTile(const TileIndex& index, BackingStoreTile* tile) const
841 {
842     return TileIndex(index.i() + tile->horizontalShift(), index.j() + tile->verticalShift());
843 }
844
845 TileIndex BackingStorePrivate::indexOfTile(const Platform::IntPoint& origin,
846                                            const Platform::IntRect& backingStoreRect) const
847 {
848     int offsetX = origin.x() - backingStoreRect.x();
849     int offsetY = origin.y() - backingStoreRect.y();
850     if (offsetX)
851         offsetX = offsetX / tileWidth();
852     if (offsetY)
853         offsetY = offsetY / tileHeight();
854     return TileIndex(offsetX, offsetY);
855 }
856
857 void BackingStorePrivate::clearAndUpdateTileOfNotRenderedRegion(const TileIndex& index, BackingStoreTile* tile,
858                                                                 const Platform::IntRectRegion& tileNotRenderedRegion,
859                                                                 const Platform::IntRect& backingStoreRect,
860                                                                 bool update)
861 {
862     // Intersect the tile with the not rendered region to get the areas
863     // of the tile that we need to clear.
864     IntRectList tileNotRenderedRegionRects = tileNotRenderedRegion.rects();
865     for (size_t i = 0; i < tileNotRenderedRegionRects.size(); ++i) {
866         Platform::IntRect tileNotRenderedRegionRect = tileNotRenderedRegionRects.at(i);
867         // Clear the render queue of this rect.
868         m_renderQueue->clear(tileNotRenderedRegionRect, true /*clearRegularRenderJobs*/);
869
870         if (update) {
871             // Add it again as a regular render job.
872             m_renderQueue->addToQueue(RenderQueue::RegularRender, tileNotRenderedRegionRect);
873         }
874
875         // Find the origin of this tile.
876         Platform::IntPoint origin = originOfTile(index, backingStoreRect);
877
878         // Map to tile coordinates.
879         tileNotRenderedRegionRect.move(-origin.x(), -origin.y());
880
881         // Clear the tile of this region.
882         tile->frontBuffer()->clearRenderedRegion(tileNotRenderedRegionRect);
883         tile->backBuffer()->clearRenderedRegion(tileNotRenderedRegionRect);
884     }
885 }
886
887 bool BackingStorePrivate::isCurrentVisibleJob(const TileIndex& index, BackingStoreTile* tile, const Platform::IntRect& backingStoreRect) const
888 {
889     // First check if the whole rect is in the queue.
890     Platform::IntRect wholeRect = Platform::IntRect(originOfTile(index, backingStoreRect), tileSize());
891     if (m_renderQueue->isCurrentVisibleScrollJob(wholeRect) || m_renderQueue->isCurrentVisibleScrollJobCompleted(wholeRect))
892         return true;
893
894     // Second check if the individual parts of the non-rendered region are in the regular queue.
895     bool isCurrent = true; // It is true until it isn't :)
896
897     IntRectList tileNotRenderedRegionRects = tile->frontBuffer()->notRenderedRegion().rects();
898     for (size_t i = 0; i < tileNotRenderedRegionRects.size(); ++i) {
899         Platform::IntRect tileNotRenderedRegionRect = tileNotRenderedRegionRects.at(i);
900         Platform::IntPoint origin = originOfTile(index, backingStoreRect);
901
902         // Map to transformed contents coordinates.
903         tileNotRenderedRegionRect.move(origin.x(), origin.y());
904
905         isCurrent = m_renderQueue->isCurrentRegularRenderJob(tileNotRenderedRegionRect) ? isCurrent : false;
906     }
907
908     return isCurrent;
909 }
910
911 void BackingStorePrivate::scrollBackingStore(int deltaX, int deltaY)
912 {
913     if (!m_webPage->isVisible())
914         return;
915
916     if (!isActive()) {
917         m_webPage->d->setShouldResetTilesWhenShown(true);
918         return;
919     }
920
921     // Calculate our new preferred matrix dimension.
922     if (deltaX || deltaY)
923         m_preferredTileMatrixDimension = abs(deltaX) > abs(deltaY) ? Horizontal : Vertical;
924
925     // Calculate our preferred matrix geometry.
926     Divisor divisor = bestDivisor(expandedContentsSize(),
927                                   tileWidth(), tileHeight(),
928                                   minimumNumberOfTilesWide(), minimumNumberOfTilesHigh(),
929                                   m_preferredTileMatrixDimension);
930
931 #if DEBUG_TILEMATRIX
932     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::scrollBackingStore divisor %dx%d",
933                            divisor.first,
934                            divisor.second);
935 #endif
936
937     // Initialize a rect with that new geometry.
938     Platform::IntRect backingStoreRect(0, 0, divisor.first * tileWidth(), divisor.second * tileHeight());
939
940     // Scroll that rect so that it fits our contents and viewport and scroll delta.
941     backingStoreRect = backingStoreRectForScroll(deltaX, deltaY, backingStoreRect);
942
943     ASSERT(!backingStoreRect.isEmpty());
944
945     setBackingStoreRect(backingStoreRect);
946 }
947
948 bool BackingStorePrivate::renderDirectToWindow(const Platform::IntRect& rect)
949 {
950     requestLayoutIfNeeded();
951
952     Platform::IntRect dirtyRect = rect;
953     dirtyRect.intersect(unclippedVisibleContentsRect());
954
955     if (dirtyRect.isEmpty())
956         return false;
957
958     Platform::IntRect screenRect = m_client->mapFromTransformedContentsToTransformedViewport(dirtyRect);
959     windowFrontBufferState()->clearBlittedRegion(screenRect);
960
961     paintDefaultBackground(dirtyRect, TransformationMatrix(), true /*flush*/);
962
963     const Platform::IntPoint origin = unclippedVisibleContentsRect().location();
964     // We don't need a buffer since we're direct rendering to window.
965     renderContents(0, origin, dirtyRect);
966     windowBackBufferState()->addBlittedRegion(screenRect);
967
968 #if USE(ACCELERATED_COMPOSITING)
969     m_isDirectRenderingAnimationMessageScheduled = false;
970
971     if (m_webPage->d->isAcceleratedCompositingActive()) {
972         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
973             BlackBerry::Platform::createMethodCallMessage(
974                 &BackingStorePrivate::drawAndBlendLayersForDirectRendering,
975                 this, dirtyRect));
976     }
977 #endif
978
979     invalidateWindow(screenRect);
980     return true;
981 }
982
983 bool BackingStorePrivate::render(const Platform::IntRect& rect)
984 {
985     if (!m_webPage->isVisible())
986         return false;
987
988     requestLayoutIfNeeded();
989
990     if (shouldDirectRenderingToWindow())
991         return renderDirectToWindow(rect);
992
993     TileRectList tileRectList = mapFromTransformedContentsToTiles(rect);
994     if (tileRectList.isEmpty())
995         return false;
996
997 #if DEBUG_BACKINGSTORE
998     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
999                            "BackingStorePrivate::render rect=(%d,%d %dx%d), m_suspendBackingStoreUpdates = %s",
1000                            rect.x(), rect.y(), rect.width(), rect.height(),
1001                            m_suspendBackingStoreUpdates ? "true" : "false");
1002 #endif
1003
1004     bool blittingDirectlyToCompositingWindow = isOpenGLCompositing();
1005
1006     BackingStoreGeometry* currentState = frontState();
1007     TileMap currentMap = currentState->tileMap();
1008
1009     Platform::IntRect dirtyContentsRect;
1010
1011     for (size_t i = 0; i < tileRectList.size(); ++i) {
1012         TileRect tileRect = tileRectList[i];
1013         TileIndex index = tileRect.first;
1014         Platform::IntRect dirtyTileRect = tileRect.second;
1015         BackingStoreTile* tile = currentMap.get(index);
1016
1017         // This dirty tile rect is in tile coordinates, but it needs to be in
1018         // transformed contents coordinates.
1019         Platform::IntRect dirtyRect = mapFromTilesToTransformedContents(tileRect);
1020
1021         // If we're not yet committed, then commit now by clearing the rendered region
1022         // and setting the committed flag as well as clearing the shift.
1023         if (!tile->isCommitted()) {
1024             tile->setCommitted(true);
1025             tile->frontBuffer()->clearRenderedRegion();
1026             tile->backBuffer()->clearRenderedRegion();
1027             tile->clearShift();
1028         }
1029
1030         // If the tile has been created, but this is the first time we are painting on it
1031         // then it hasn't been given a default background yet so that we can save time during
1032         // startup. That's why we are doing it here instead...
1033         if (!tile->backgroundPainted())
1034             tile->paintBackground();
1035
1036         // Paint default background if contents rect is empty.
1037         if (!expandedContentsRect().isEmpty()) {
1038             // Otherwise we should clip the contents size and render the content.
1039             dirtyRect.intersect(expandedContentsRect());
1040
1041             dirtyTileRect.intersect(tileContentsRect(index, expandedContentsRect(), currentState));
1042
1043             // We probably have extra tiles since the contents size is so small.
1044             // Save some cycles here...
1045             if (dirtyRect.isEmpty())
1046                 continue;
1047         }
1048
1049         copyPreviousContentsToBackSurfaceOfTile(dirtyTileRect, tile);
1050
1051         BlackBerry::Platform::Graphics::Buffer* nativeBuffer
1052             = tile->backBuffer()->nativeBuffer();
1053
1054         if (blittingDirectlyToCompositingWindow) {
1055             pthread_mutex_lock(&m_blitGenerationLock);
1056             while (m_blitGeneration == tile->backBuffer()->blitGeneration()) {
1057                 int err = pthread_cond_timedwait(&m_blitGenerationCond, &m_blitGenerationLock, &m_currentBlitEnd);
1058                 if (err == ETIMEDOUT) {
1059                     ++m_blitGeneration;
1060                     break;
1061                 }
1062                 if (err) {
1063                     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1064                                               "cond_timedwait failed (%s)", strerror(err));
1065                     break;
1066                 }
1067             }
1068             pthread_mutex_unlock(&m_blitGenerationLock);
1069         }
1070
1071         // FIXME: modify render to take a Vector<IntRect> parameter so we're not recreating
1072         // GraphicsContext on the stack each time.
1073         renderContents(nativeBuffer, originOfTile(index), dirtyRect);
1074
1075         // Add the newly rendered region to the tile so it can keep track for blits.
1076         tile->backBuffer()->addRenderedRegion(dirtyTileRect);
1077
1078         // Check if the contents for this tile's backbuffer are valid when
1079         // compared to the front buffer.
1080         bool backBufferIsValid = tile->backBuffer()->isRendered(tile->frontBuffer()->renderedRegion());
1081
1082         // Our current design demands that the backbuffer is valid after any
1083         // rendering operation so assert that here. If we hit this assert we
1084         // know that we're doing something bad that will result in artifacts.
1085         ASSERT(backBufferIsValid);
1086
1087         // We will need a swap here because of the shared back buffer.
1088         if (backBufferIsValid) {
1089             tile->swapBuffers();
1090             BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
1091             tile->backBuffer()->clearRenderedRegion();
1092         }
1093
1094         dirtyContentsRect = Platform::unionOfRects(dirtyContentsRect, dirtyRect);
1095     }
1096
1097     return true;
1098 }
1099
1100 void BackingStorePrivate::requestLayoutIfNeeded() const
1101 {
1102     m_webPage->d->requestLayoutIfNeeded();
1103 }
1104
1105 bool BackingStorePrivate::renderVisibleContents()
1106 {
1107     Platform::IntRect renderRect = shouldDirectRenderingToWindow() ? visibleContentsRect() : visibleTilesRect();
1108     if (render(renderRect)) {
1109         m_renderQueue->clear(renderRect, true /*clearRegularRenderJobs*/);
1110         return true;
1111     }
1112     return false;
1113 }
1114
1115 bool BackingStorePrivate::renderBackingStore()
1116 {
1117     return render(frontState()->backingStoreRect());
1118 }
1119
1120 void BackingStorePrivate::blitVisibleContents(bool force)
1121 {
1122     // Blitting must never happen for direct rendering case.
1123     ASSERT(!shouldDirectRenderingToWindow());
1124     if (shouldDirectRenderingToWindow())
1125         return;
1126
1127     if (m_suspendScreenUpdates) {
1128         // Avoid client going into busy loop while updates suspended.
1129         if (force)
1130             m_hasBlitJobs = false;
1131         return;
1132     }
1133
1134     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) {
1135 #if USE(ACCELERATED_COMPOSITING)
1136         // The blit will call drawSubLayers if necessary
1137         m_needsDrawLayersOnCommit = false;
1138 #endif
1139
1140         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
1141             BlackBerry::Platform::createMethodCallMessage(
1142                 &BackingStorePrivate::blitVisibleContents, this, force));
1143         return;
1144     }
1145
1146     blitContents(m_webPage->client()->userInterfaceBlittedDestinationRect(),
1147                  m_webPage->client()->userInterfaceBlittedVisibleContentsRect(),
1148                  force);
1149 }
1150
1151 void BackingStorePrivate::copyPreviousContentsToBackSurfaceOfWindow()
1152 {
1153     Platform::IntRectRegion previousContentsRegion
1154         = Platform::IntRectRegion::subtractRegions(windowFrontBufferState()->blittedRegion(), windowBackBufferState()->blittedRegion());
1155
1156     if (previousContentsRegion.isEmpty())
1157         return;
1158
1159     if (Window* window = m_webPage->client()->window())
1160         window->copyFromFrontToBack(previousContentsRegion);
1161     windowBackBufferState()->addBlittedRegion(previousContentsRegion);
1162 }
1163
1164 void BackingStorePrivate::copyPreviousContentsToBackSurfaceOfTile(const Platform::IntRect& rect,
1165                                                                   BackingStoreTile* tile)
1166 {
1167     Platform::IntRectRegion previousContentsRegion
1168         = Platform::IntRectRegion::subtractRegions(tile->frontBuffer()->renderedRegion(), rect);
1169
1170     IntRectList previousContentsRects = previousContentsRegion.rects();
1171     for (size_t i = 0; i < previousContentsRects.size(); ++i) {
1172         Platform::IntRect previousContentsRect = previousContentsRects.at(i);
1173         tile->backBuffer()->addRenderedRegion(previousContentsRect);
1174
1175         BlackBerry::Platform::Graphics::blitToBuffer(
1176             tile->backBuffer()->nativeBuffer(), previousContentsRect,
1177             tile->frontBuffer()->nativeBuffer(), previousContentsRect);
1178     }
1179 }
1180
1181 void BackingStorePrivate::paintDefaultBackground(const Platform::IntRect& contents,
1182                                                  const WebCore::TransformationMatrix& transformation,
1183                                                  bool flush)
1184 {
1185     const Platform::IntRect contentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_webPage->d->transformedContentsSize());
1186     Platform::IntPoint origin = contents.location();
1187     Platform::IntRect contentsClipped = contents;
1188
1189     // We have to paint the default background in the case of overzoom and
1190     // make sure it is invalidated.
1191     Color color(m_webPage->settings()->overZoomColor());
1192
1193     Platform::IntRectRegion overScrollRegion
1194             = Platform::IntRectRegion::subtractRegions(Platform::IntRect(contentsClipped), contentsRect);
1195
1196     IntRectList overScrollRects = overScrollRegion.rects();
1197     for (size_t i = 0; i < overScrollRects.size(); ++i) {
1198         Platform::IntRect overScrollRect = overScrollRects.at(i);
1199         overScrollRect.move(-origin.x(), -origin.y());
1200         overScrollRect = transformation.mapRect(overScrollRect);
1201
1202         if (!transformation.isIdentity()) {
1203             // Because of rounding it is possible that overScrollRect could be off-by-one larger
1204             // than the surface size of the window. We prevent this here, by clamping
1205             // it to ensure that can't happen.
1206             overScrollRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
1207         }
1208
1209         clearWindow(overScrollRect, color.red(), color.green(), color.blue(), color.alpha());
1210     }
1211 }
1212
1213 void BackingStorePrivate::blitContents(const Platform::IntRect& dstRect,
1214                                        const Platform::IntRect& srcRect,
1215                                        bool force)
1216 {
1217     // Blitting must never happen for direct rendering case.
1218     // Use invalidateWindow() instead.
1219     ASSERT(!shouldDirectRenderingToWindow());
1220     if (shouldDirectRenderingToWindow())
1221         return;
1222
1223     if (!m_webPage->isVisible() || m_suspendScreenUpdates || !isActive()) {
1224         // Avoid client going into busy loop while blit is impossible.
1225         if (force)
1226             m_hasBlitJobs = false;
1227         return;
1228     }
1229
1230     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) {
1231 #if USE(ACCELERATED_COMPOSITING)
1232         // The blit will call drawSubLayers if necessary
1233         m_needsDrawLayersOnCommit = false;
1234 #endif
1235
1236         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
1237             BlackBerry::Platform::createMethodCallMessage(
1238                 &BackingStorePrivate::blitContents, this, dstRect, srcRect, force));
1239         return;
1240     }
1241
1242     if (m_defersBlit && !force) {
1243         m_hasBlitJobs = true;
1244         return;
1245     }
1246
1247     m_hasBlitJobs = false;
1248
1249     BackingStoreGeometry* currentState = frontState();
1250     const Platform::IntRect contentsRect = Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize());
1251
1252 #if DEBUG_VISUALIZE
1253     // Substitute a debugRect that consists of the union of the backingstore rect
1254     // and the ui thread viewport rect instead of the normal source rect so we
1255     // can visualize the entire backingstore and what it is doing when we
1256     // scroll and zoom!
1257     // FIXME: This should not explicitely depend on WebCore::.
1258     WebCore::IntRect debugRect = currentState->backingStoreRect();
1259     debugRect.unite(m_webPage->client()->userInterfaceBlittedVisibleContentsRect());
1260     if (debugRect.width() < debugRect.height())
1261         debugRect.setWidth(ceil(double(srcRect.width()) * (double(debugRect.height()) / srcRect.height())));
1262     if (debugRect.height() < debugRect.width())
1263         debugRect.setHeight(ceil(double(srcRect.height()) * (double(debugRect.width()) / srcRect.width())));
1264     Platform::IntRect contents = debugRect;
1265 #else
1266     Platform::IntRect contents = srcRect;
1267 #endif
1268
1269     // FIXME: This should not explicitely depend on WebCore::.
1270     TransformationMatrix transformation;
1271     if (!contents.isEmpty())
1272         transformation = TransformationMatrix::rectToRect(FloatRect(FloatPoint(0.0, 0.0), WebCore::IntSize(contents.size())), WebCore::IntRect(dstRect));
1273
1274     bool blittingDirectlyToCompositingWindow = isOpenGLCompositing();
1275 #if USE(ACCELERATED_COMPOSITING)
1276     BackingStoreCompositingSurface* compositingSurface =
1277         SurfacePool::globalSurfacePool()->compositingSurface();
1278
1279     if (!blittingDirectlyToCompositingWindow)
1280         drawSubLayers();
1281 #endif
1282
1283 #if DEBUG_BACKINGSTORE
1284     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1285                            "BackingStorePrivate::blitContents dstRect=(%d,%d %dx%d) srcRect=(%d,%d %dx%d)",
1286                            dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height(),
1287                            srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height());
1288 #endif
1289
1290     Platform::IntPoint origin = contents.location();
1291     Platform::IntRect contentsClipped = contents;
1292
1293     paintDefaultBackground(contents, transformation, false /*flush*/);
1294
1295     TileMap currentMap = currentState->tileMap();
1296
1297 #if DEBUG_CHECKERBOARD
1298     bool blitCheckered = false;
1299 #endif
1300
1301     // Don't clip to contents if it is empty so we can still paint default background.
1302     if (!contentsRect.isEmpty()) {
1303         contentsClipped.intersect(contentsRect);
1304         if (contentsClipped.isEmpty()) {
1305             invalidateWindow(dstRect);
1306             return;
1307         }
1308
1309         Platform::IntRectRegion contentsRegion = contentsClipped;
1310         Platform::IntRectRegion backingStoreRegion = currentState->backingStoreRect();
1311         Platform::IntRectRegion checkeredRegion
1312             = Platform::IntRectRegion::subtractRegions(contentsRegion, backingStoreRegion);
1313
1314         // Blit checkered to those parts that are not covered by the backingStoreRect.
1315         IntRectList checkeredRects = checkeredRegion.rects();
1316         for (size_t i = 0; i < checkeredRects.size(); ++i) {
1317             Platform::IntRect dstRect = transformation.mapRect(Platform::IntRect(
1318                 Platform::IntPoint(checkeredRects.at(i).x() - origin.x(), checkeredRects.at(i).y() - origin.y()),
1319                                    checkeredRects.at(i).size()));
1320 #if DEBUG_CHECKERBOARD
1321             blitCheckered = true;
1322 #endif
1323             checkerWindow(dstRect, checkeredRects.at(i).location(), transformation.a());
1324         }
1325     }
1326
1327     Vector<TileBuffer*> blittedTiles;
1328
1329     // Get the list of tile rects that makeup the content.
1330     TileRectList tileRectList = mapFromTransformedContentsToTiles(contentsClipped, currentState);
1331     for (size_t i = 0; i < tileRectList.size(); ++i) {
1332         TileRect tileRect = tileRectList[i];
1333         TileIndex index = tileRect.first;
1334         Platform::IntRect dirtyTileRect = tileRect.second;
1335         BackingStoreTile* tile = currentMap.get(index);
1336         TileBuffer* tileBuffer = tile->frontBuffer();
1337
1338         // This dirty rect is in tile coordinates, but it needs to be in
1339         // transformed contents coordinates.
1340         Platform::IntRect dirtyRect
1341             = mapFromTilesToTransformedContents(tileRect, currentState->backingStoreRect());
1342
1343         // Don't clip to contents if it is empty so we can still paint default background.
1344         if (!contentsRect.isEmpty()) {
1345             // Otherwise we should clip the contents size and blit.
1346             dirtyRect.intersect(contentsRect);
1347
1348             // We probably have extra tiles since the contents size is so small.
1349             // Save some cycles here...
1350             if (dirtyRect.isEmpty())
1351                 continue;
1352         }
1353
1354         // Now, this dirty rect is in transformed coordinates relative to the
1355         // transformed contents, but ultimately it needs to be transformed
1356         // coordinates relative to the viewport.
1357         dirtyRect.move(-origin.x(), -origin.y());
1358
1359         // Save some cycles here...
1360         if (dirtyRect.isEmpty() || dirtyTileRect.isEmpty())
1361             continue;
1362
1363         TileRect wholeTileRect;
1364         wholeTileRect.first = index;
1365         wholeTileRect.second = this->tileRect();
1366
1367         bool committed = tile->isCommitted();
1368         bool rendered = tileBuffer->isRendered(dirtyTileRect);
1369         bool paintCheckered = !committed || !rendered;
1370
1371         if (paintCheckered) {
1372             Platform::IntRect dirtyRectT = transformation.mapRect(dirtyRect);
1373
1374             if (!transformation.isIdentity()) {
1375                 // Because of rounding it is possible that dirtyRect could be off-by-one larger
1376                 // than the surface size of the dst buffer. We prevent this here, by clamping
1377                 // it to ensure that can't happen.
1378                 dirtyRectT.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
1379             }
1380             const Platform::IntPoint contentsOrigin(dirtyRect.x() + origin.x(), dirtyRect.y() + origin.y());
1381 #if DEBUG_CHECKERBOARD
1382             blitCheckered = true;
1383 #endif
1384             checkerWindow(dirtyRectT, contentsOrigin, transformation.a());
1385         }
1386
1387         // Blit the visible buffer here if we have visible zoom jobs.
1388         if (m_renderQueue->hasCurrentVisibleZoomJob()) {
1389
1390             // Needs to be in same coordinate system as dirtyRect.
1391             Platform::IntRect visibleTileBufferRect = m_visibleTileBufferRect;
1392             visibleTileBufferRect.move(-origin.x(), -origin.y());
1393
1394             // Clip to the visibleTileBufferRect.
1395             dirtyRect.intersect(visibleTileBufferRect);
1396
1397             // Clip to the dirtyRect.
1398             visibleTileBufferRect.intersect(dirtyRect);
1399
1400             if (!dirtyRect.isEmpty() && !visibleTileBufferRect.isEmpty()) {
1401                 BackingStoreTile* visibleTileBuffer
1402                     = SurfacePool::globalSurfacePool()->visibleTileBuffer();
1403                 ASSERT(visibleTileBuffer->size() == visibleContentsRect().size());
1404
1405                 // The offset of the current viewport with the visble tile buffer.
1406                 Platform::IntPoint difference = origin - m_visibleTileBufferRect.location();
1407                 Platform::IntSize offset = Platform::IntSize(difference.x(), difference.y());
1408
1409                 // Map to the visibleTileBuffer coordinates.
1410                 Platform::IntRect dirtyTileRect = visibleTileBufferRect;
1411                 dirtyTileRect.move(offset.width(), offset.height());
1412
1413                 Platform::IntRect dirtyRectT = transformation.mapRect(dirtyRect);
1414
1415                 if (!transformation.isIdentity()) {
1416                     // Because of rounding it is possible that dirtyRect could be off-by-one larger
1417                     // than the surface size of the dst buffer. We prevent this here, by clamping
1418                     // it to ensure that can't happen.
1419                     dirtyRectT.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
1420                 }
1421
1422                 blitToWindow(dirtyRectT,
1423                              visibleTileBuffer->frontBuffer()->nativeBuffer(),
1424                              dirtyTileRect,
1425                              false /*blend*/, 255);
1426             }
1427         } else if (committed) {
1428             // Intersect the rendered region.
1429             Platform::IntRectRegion renderedRegion = tileBuffer->renderedRegion();
1430             IntRectList dirtyRenderedRects = renderedRegion.rects();
1431             for (size_t i = 0; i < dirtyRenderedRects.size(); ++i) {
1432                 TileRect tileRect;
1433                 tileRect.first = index;
1434                 tileRect.second = intersection(dirtyTileRect, dirtyRenderedRects.at(i));
1435                 if (tileRect.second.isEmpty())
1436                     continue;
1437                 // Blit the rendered parts.
1438                 blitTileRect(tileBuffer, tileRect, origin, transformation, currentState);
1439             }
1440             blittedTiles.append(tileBuffer);
1441         }
1442     }
1443
1444 #if USE(ACCELERATED_COMPOSITING)
1445     if (blittingDirectlyToCompositingWindow) {
1446         WebCore::FloatRect contentsRect = m_webPage->d->mapFromTransformedFloatRect(
1447             WebCore::FloatRect(WebCore::IntRect(contents)));
1448         m_webPage->d->drawSubLayers(dstRect, contentsRect);
1449     } else if (compositingSurface)
1450         blendCompositingSurface(dstRect);
1451
1452 #endif
1453
1454 #if ENABLE_SCROLLBARS
1455     if (isScrollingOrZooming() && m_client->isMainFrame()) {
1456         if (m_client->scrollsHorizontally())
1457             blitHorizontalScrollbar(origin);
1458         if (m_client->scrollsVertically())
1459             blitVerticalScrollbar(origin);
1460     }
1461 #endif
1462
1463 #if DEBUG_VISUALIZE
1464     // FIXME: This should not explicitely depend on WebCore::.
1465     BlackBerry::Platform::Graphics::Buffer* windowBuffer = buffer();
1466     BlackBerry::Platform::Graphics::Drawable* bufferDrawable =
1467         BlackBerry::Platform::Graphics::lockBufferDrawable(windowBuffer);
1468     PlatformGraphicsContext* bufferPlatformGraphicsContext =
1469         SurfacePool::globalSurfacePool()->createPlatformGraphicsContext(bufferDrawable);
1470     GraphicsContext graphicsContext(bufferPlatformGraphicsContext);
1471     FloatRect wkViewport = FloatRect(visibleContentsRect());
1472     FloatRect uiViewport = FloatRect(m_webPage->client()->userInterfaceBlittedVisibleContentsRect());
1473     wkViewport.move(-contents.x(), -contents.y());
1474     uiViewport.move(-contents.x(), -contents.y());
1475
1476     graphicsContext.save();
1477
1478     // Draw a blue rect for the webkit thread viewport.
1479     graphicsContext.setStrokeColor(WebCore::Color(0, 0, 255), WebCore::ColorSpaceDeviceRGB);
1480     graphicsContext.strokeRect(transformation.mapRect(wkViewport), 1.0);
1481
1482     // Draw a red rect for the ui thread viewport.
1483     graphicsContext.setStrokeColor(WebCore::Color(255, 0, 0), WebCore::ColorSpaceDeviceRGB);
1484     graphicsContext.strokeRect(transformation.mapRect(uiViewport), 1.0);
1485
1486     graphicsContext.restore();
1487
1488     delete bufferPlatformGraphicsContext;
1489     releaseBufferDrawable(windowBuffer);
1490 #endif
1491
1492 #if DEBUG_CHECKERBOARD
1493     static double lastCheckeredTime = 0;
1494
1495     if (blitCheckered && !lastCheckeredTime) {
1496         lastCheckeredTime = WTF::currentTime();
1497         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1498             "Blitting checkered pattern at %f\n", lastCheckeredTime);
1499     } else if (blitCheckered && lastCheckeredTime) {
1500         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1501             "Blitting checkered pattern at %f\n", WTF::currentTime());
1502     } else if (!blitCheckered && lastCheckeredTime) {
1503         double time = WTF::currentTime();
1504         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1505             "Blitting over checkered pattern at %f took %f\n", time, time - lastCheckeredTime);
1506         lastCheckeredTime = 0;
1507     }
1508 #endif
1509
1510     invalidateWindow(dstRect);
1511
1512     if (blittingDirectlyToCompositingWindow) {
1513         pthread_mutex_lock(&m_blitGenerationLock);
1514
1515         ++m_blitGeneration;
1516         for (unsigned int i = 0; i < blittedTiles.size(); ++i)
1517             blittedTiles[i]->setBlitGeneration(m_blitGeneration);
1518
1519         clock_gettime(CLOCK_REALTIME, &m_currentBlitEnd);
1520         m_currentBlitEnd.tv_nsec += 30 * 1000 * 1000;
1521         if (m_currentBlitEnd.tv_nsec >= 1000000000L) {
1522             m_currentBlitEnd.tv_sec  += 1;
1523             m_currentBlitEnd.tv_nsec -= 1000000000L;
1524         }
1525
1526         pthread_mutex_unlock(&m_blitGenerationLock);
1527         pthread_cond_signal(&m_blitGenerationCond);
1528     }
1529 }
1530
1531 Platform::IntRect BackingStorePrivate::blitTileRect(TileBuffer* tileBuffer,
1532                                                    const TileRect& tileRect,
1533                                                    const Platform::IntPoint& origin,
1534                                                    const WebCore::TransformationMatrix& matrix,
1535                                                    BackingStoreGeometry* state)
1536 {
1537     if (!m_webPage->isVisible() || !isActive())
1538         return Platform::IntRect();
1539
1540     Platform::IntRect dirtyTileRect = tileRect.second;
1541
1542     // This dirty rect is in tile coordinates, but it needs to be in
1543     // transformed contents coordinates.
1544     Platform::IntRect dirtyRect = mapFromTilesToTransformedContents(tileRect, state->backingStoreRect());
1545
1546     // Now, this dirty rect is in transformed coordinates relative to the
1547     // transformed contents, but ultimately it needs to be transformed
1548     // coordinates relative to the viewport.
1549     dirtyRect.move(-origin.x(), -origin.y());
1550     dirtyRect = matrix.mapRect(dirtyRect);
1551
1552     if (!matrix.isIdentity()) {
1553         // Because of rounding it is possible that dirtyRect could be off-by-one larger
1554         // than the surface size of the dst buffer. We prevent this here, by clamping
1555         // it to ensure that can't happen.
1556         dirtyRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), surfaceSize()));
1557     }
1558
1559     ASSERT(!dirtyRect.isEmpty());
1560     ASSERT(!dirtyTileRect.isEmpty());
1561     if (dirtyRect.isEmpty() || dirtyTileRect.isEmpty())
1562         return Platform::IntRect();
1563
1564     blitToWindow(dirtyRect, tileBuffer->nativeBuffer(), dirtyTileRect,
1565                  false /*blend*/, 255);
1566     return dirtyRect;
1567 }
1568
1569 #if USE(ACCELERATED_COMPOSITING)
1570 void BackingStorePrivate::blendCompositingSurface(const Platform::IntRect& dstRect)
1571 {
1572     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread()) {
1573         typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&);
1574         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
1575             BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate, Platform::IntRect>(
1576                 &BackingStorePrivate::blendCompositingSurface, this, dstRect));
1577         return;
1578     }
1579
1580     BackingStoreCompositingSurface* compositingSurface =
1581         SurfacePool::globalSurfacePool()->compositingSurface();
1582
1583     if (!compositingSurface || !m_webPage->isVisible())
1584         return;
1585
1586     WebCore::LayerRenderingResults lastCompositingResults = m_webPage->d->lastCompositingResults();
1587     for (size_t i = 0; i < lastCompositingResults.holePunchRectSize(); i++) {
1588         Platform::IntRect holePunchRect = lastCompositingResults.holePunchRect(i);
1589
1590         holePunchRect.intersect(dstRect);
1591         holePunchRect.intersect(Platform::IntRect(
1592             Platform::IntPoint(0, 0), surfaceSize()));
1593
1594         if (!holePunchRect.isEmpty())
1595             clearWindow(holePunchRect, 0, 0, 0, 0);
1596     }
1597
1598     CompositingSurfaceBuffer* frontBuffer = compositingSurface->frontBuffer();
1599
1600     IntRectList rects = lastCompositingResults.dirtyRegion.rects();
1601     for (size_t i = 0; i < rects.size(); ++i) {
1602         rects[i].intersect(dstRect);
1603 #if DEBUG_COMPOSITING_DIRTY_REGION
1604         clearBuffer(buffer(), rects[i], 255, 0, 0, 128);
1605 #endif
1606         blitToWindow(rects[i], frontBuffer->nativeBuffer(), rects[i], true /*blend*/, 255);
1607     }
1608 }
1609
1610 void BackingStorePrivate::clearCompositingSurface()
1611 {
1612     BackingStoreCompositingSurface* compositingSurface =
1613         SurfacePool::globalSurfacePool()->compositingSurface();
1614
1615     if (!compositingSurface)
1616         return;
1617
1618     CompositingSurfaceBuffer* frontBuffer = compositingSurface->frontBuffer();
1619     BlackBerry::Platform::Graphics::clearBuffer(frontBuffer->nativeBuffer(), Platform::IntRect(Platform::IntPoint(), frontBuffer->surfaceSize()), 0, 0, 0, 0);
1620 }
1621 #endif // USE(ACCELERATED_COMPOSITING)
1622
1623 void BackingStorePrivate::blitHorizontalScrollbar(const Platform::IntPoint& scrollPosition)
1624 {
1625     if (!m_webPage->isVisible())
1626         return;
1627
1628     ASSERT(m_client->scrollsHorizontally());
1629
1630     m_webPage->client()->drawHorizontalScrollbar();
1631 }
1632
1633 void BackingStorePrivate::blitVerticalScrollbar(const Platform::IntPoint& scrollPosition)
1634 {
1635     if (!m_webPage->isVisible())
1636         return;
1637
1638     ASSERT(m_client->scrollsVertically());
1639
1640     m_webPage->client()->drawVerticalScrollbar();
1641 }
1642
1643 bool BackingStorePrivate::isTileVisible(const TileIndex& index) const
1644 {
1645     TileRect tileRect;
1646     tileRect.first = index;
1647     tileRect.second = this->tileRect();
1648     return mapFromTilesToTransformedContents(tileRect).intersects(visibleContentsRect());
1649 }
1650
1651 bool BackingStorePrivate::isTileVisible(const Platform::IntPoint& origin) const
1652 {
1653     return Platform::IntRect(origin, tileSize()).intersects(visibleContentsRect());
1654 }
1655
1656 Platform::IntRect BackingStorePrivate::visibleTilesRect() const
1657 {
1658     BackingStoreGeometry* currentState = frontState();
1659     TileMap currentMap = currentState->tileMap();
1660
1661     Platform::IntRect rect;
1662     TileMap::const_iterator end = currentMap.end();
1663     for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
1664         TileRect tileRect;
1665         tileRect.first = it->first;
1666         tileRect.second = this->tileRect();
1667         Platform::IntRect tile = mapFromTilesToTransformedContents(tileRect);
1668         if (tile.intersects(visibleContentsRect()))
1669             rect = Platform::unionOfRects(rect, tile);
1670     }
1671     return rect;
1672 }
1673
1674 Platform::IntRect BackingStorePrivate::tileVisibleContentsRect(const TileIndex& index) const
1675 {
1676     if (!isTileVisible(index))
1677         return Platform::IntRect();
1678
1679     return tileContentsRect(index, visibleContentsRect());
1680 }
1681
1682 Platform::IntRect BackingStorePrivate::tileUnclippedVisibleContentsRect(const TileIndex& index) const
1683 {
1684     if (!isTileVisible(index))
1685         return Platform::IntRect();
1686
1687     return tileContentsRect(index, unclippedVisibleContentsRect());
1688 }
1689
1690 Platform::IntRect BackingStorePrivate::tileContentsRect(const TileIndex& index,
1691                                                        const Platform::IntRect& contents) const
1692 {
1693     return tileContentsRect(index, contents, frontState());
1694 }
1695
1696 Platform::IntRect BackingStorePrivate::tileContentsRect(const TileIndex& index,
1697                                                        const Platform::IntRect& contents,
1698                                                        BackingStoreGeometry* state) const
1699 {
1700     TileRectList tileRectList = mapFromTransformedContentsToTiles(contents, state);
1701     for (size_t i = 0; i < tileRectList.size(); ++i) {
1702         TileRect tileRect = tileRectList[i];
1703         if (index == tileRect.first)
1704             return tileRect.second;
1705     }
1706     return Platform::IntRect();
1707 }
1708
1709 void BackingStorePrivate::resetRenderQueue()
1710 {
1711     m_renderQueue->reset();
1712 }
1713
1714 void BackingStorePrivate::clearVisibleZoom()
1715 {
1716     m_renderQueue->clearVisibleZoom();
1717 }
1718
1719 void BackingStorePrivate::resetTiles(bool resetBackground)
1720 {
1721     BackingStoreGeometry* currentState = frontState();
1722     TileMap currentMap = currentState->tileMap();
1723
1724     TileMap::const_iterator end = currentMap.end();
1725     for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it)
1726         resetTile(it->first, it->second, resetBackground);
1727 }
1728
1729 void BackingStorePrivate::updateTiles(bool updateVisible, bool immediate)
1730 {
1731     if (!isActive())
1732         return;
1733
1734     BackingStoreGeometry* currentState = frontState();
1735     TileMap currentMap = currentState->tileMap();
1736
1737     TileMap::const_iterator end = currentMap.end();
1738     for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
1739         bool isVisible = isTileVisible(it->first);
1740         if (!updateVisible && isVisible)
1741             continue;
1742         updateTile(it->first, immediate);
1743     }
1744 }
1745
1746 void BackingStorePrivate::updateTilesForScrollOrNotRenderedRegion(bool checkLoading)
1747 {
1748     // This method looks at all the tiles and if they are visible, but not completely
1749     // rendered or we are loading, then it updates them. For all tiles, visible and
1750     // non-visible, if a previous attempt was made to render them during a regular
1751     // render job, but they were not visible at the time, then update them and if
1752     // they are currently visible, reset them.
1753
1754     BackingStoreGeometry* currentState = frontState();
1755     TileMap currentMap = currentState->tileMap();
1756     Platform::IntRect backingStoreRect = currentState->backingStoreRect();
1757
1758     bool isLoading = m_client->loadState() == WebPagePrivate::Committed;
1759     bool forceVisible = checkLoading && isLoading;
1760
1761     TileMap::const_iterator end = currentMap.end();
1762     for (TileMap::const_iterator it = currentMap.begin(); it != end; ++it) {
1763         TileIndex index = it->first;
1764         BackingStoreTile* tile = it->second;
1765         bool isVisible = isTileVisible(index);
1766         // The rect in transformed contents coordinates.
1767         Platform::IntRect rect(originOfTile(index), tileSize());
1768         if (tile->isCommitted()
1769             && m_renderQueue->regularRenderJobsPreviouslyAttemptedButNotRendered(rect)) {
1770             // If the render queue previously tried to render this tile, but the
1771             // tile wasn't visible at the time we can't simply restore the tile
1772             // since the content is now invalid as far as WebKit is concerned.
1773             // Instead, we clear that part of the tile if it is visible and then
1774             // put the tile in the render queue again.
1775             if (isVisible) {
1776                 // Intersect the tile with the not rendered region to get the areas
1777                 // of the tile that we need to clear.
1778                 Platform::IntRectRegion tileNotRenderedRegion
1779                     = Platform::IntRectRegion::intersectRegions(
1780                         m_renderQueue->regularRenderJobsNotRenderedRegion(),
1781                         rect);
1782                 clearAndUpdateTileOfNotRenderedRegion(index,
1783                                                       tile,
1784                                                       tileNotRenderedRegion,
1785                                                       backingStoreRect,
1786                                                       false /*update*/);
1787 #if DEBUG_BACKINGSTORE
1788                 Platform::IntRect extents = tileNotRenderedRegion.extents();
1789                 BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
1790                     "BackingStorePrivate::updateTilesForScroll did clear tile %d,%d %dx%d",
1791                     extents.x(), extents.y(), extents.width(), extents.height());
1792 #endif
1793             }
1794             updateTile(index, false /*immediate*/);
1795         } else if (isVisible
1796             && (forceVisible || !tile->frontBuffer()->isRendered(tileVisibleContentsRect(index)))
1797             && !isCurrentVisibleJob(index, tile, backingStoreRect))
1798             updateTile(index, false /*immediate*/);
1799     }
1800 }
1801
1802 void BackingStorePrivate::resetTile(const TileIndex& index, BackingStoreTile* tile, bool resetBackground)
1803 {
1804     if (!m_webPage->isVisible())
1805         return;
1806
1807     if (!isActive()) {
1808         m_webPage->d->setShouldResetTilesWhenShown(true);
1809         return;
1810     }
1811
1812     TileRect tileRect;
1813     tileRect.first = index;
1814     tileRect.second = this->tileRect();
1815     // Only clear regular render jobs if we're clearing the background too.
1816     m_renderQueue->clear(mapFromTilesToTransformedContents(tileRect), resetBackground /*clearRegularRenderJobs*/);
1817     if (resetBackground)
1818         tile->reset();
1819 }
1820
1821 void BackingStorePrivate::updateTile(const TileIndex& index, bool immediate)
1822 {
1823     if (!isActive())
1824         return;
1825
1826     TileRect tileRect;
1827     tileRect.first = index;
1828     tileRect.second = this->tileRect();
1829     Platform::IntRect updateRect = mapFromTilesToTransformedContents(tileRect);
1830     RenderQueue::JobType jobType = isTileVisible(index) ? RenderQueue::VisibleScroll : RenderQueue::NonVisibleScroll;
1831     if (immediate)
1832         render(updateRect);
1833     else
1834         m_renderQueue->addToQueue(jobType, updateRect);
1835 }
1836
1837 void BackingStorePrivate::updateTile(const Platform::IntPoint& origin, bool immediate)
1838 {
1839     if (!isActive())
1840         return;
1841
1842     Platform::IntRect updateRect = Platform::IntRect(origin, tileSize());
1843     RenderQueue::JobType jobType = isTileVisible(origin) ? RenderQueue::VisibleScroll : RenderQueue::NonVisibleScroll;
1844     if (immediate)
1845         render(updateRect);
1846     else
1847         m_renderQueue->addToQueue(jobType, updateRect);
1848 }
1849
1850 Platform::IntRect BackingStorePrivate::mapFromTilesToTransformedContents(const BackingStorePrivate::TileRect& tileRect) const
1851 {
1852     return mapFromTilesToTransformedContents(tileRect, frontState()->backingStoreRect());
1853 }
1854
1855 Platform::IntRect BackingStorePrivate::mapFromTilesToTransformedContents(const BackingStorePrivate::TileRect& tileRect, const Platform::IntRect& backingStoreRect) const
1856 {
1857     TileIndex index = tileRect.first;
1858     Platform::IntRect rect = tileRect.second;
1859     // The origin of the tile including the backing store offset.
1860     const Platform::IntPoint originOfTile = this->originOfTile(index, backingStoreRect);
1861     rect.move(originOfTile.x(), originOfTile.y());
1862     return rect;
1863 }
1864
1865 BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToAbsoluteTileBoundaries(const Platform::IntRect& rect) const
1866 {
1867     if (!m_webPage->isVisible() || !isActive()) {
1868         ASSERT_NOT_REACHED();
1869         return TileRectList();
1870     }
1871
1872     TileRectList tileRectList;
1873     int firstXOffset = rect.x() / tileWidth();
1874     int firstYOffset = rect.y() / tileHeight();
1875     int lastXOffset = (rect.right() - 1) / tileWidth();
1876     int lastYOffset = (rect.bottom() - 1) / tileHeight();
1877     for (int i = firstXOffset; i <= lastXOffset; ++i) {
1878         for (int j = firstYOffset; j <= lastYOffset; ++j) {
1879             const int dstX = (i == firstXOffset) ? rect.x() : i * tileWidth();
1880             const int dstY = (j == firstYOffset) ? rect.y() : j * tileHeight();
1881             const int dstRight = (i == lastXOffset) ? rect.right() : (i + 1) * tileWidth();
1882             const int dstBottom = (j == lastYOffset) ? rect.bottom() : (j + 1) * tileHeight();
1883             const int srcX = dstX % tileWidth();
1884             const int srcY = dstY % tileHeight();
1885             TileRect tileRect;
1886             tileRect.first = TileIndex(i, j);
1887             tileRect.second = Platform::IntRect(srcX, srcY, dstRight - dstX, dstBottom - dstY);
1888             tileRectList.append(tileRect);
1889         }
1890     }
1891     return tileRectList;
1892 }
1893
1894
1895 BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToTiles(const Platform::IntRect& rect) const
1896 {
1897     return mapFromTransformedContentsToTiles(rect, frontState());
1898 }
1899
1900 BackingStorePrivate::TileRectList BackingStorePrivate::mapFromTransformedContentsToTiles(const Platform::IntRect& rect, BackingStoreGeometry* state) const
1901 {
1902     TileMap tileMap = state->tileMap();
1903
1904     TileRectList tileRectList;
1905     TileMap::const_iterator end = tileMap.end();
1906     for (TileMap::const_iterator it = tileMap.begin(); it != end; ++it) {
1907         TileIndex index = it->first;
1908         BackingStoreTile* tile = it->second;
1909
1910         // Need to map the rect to tile coordinates.
1911         Platform::IntRect r = rect;
1912
1913         // The origin of the tile including the backing store offset.
1914         const Platform::IntPoint originOfTile = this->originOfTile(index, state->backingStoreRect());
1915
1916         r.move(-(originOfTile.x()), -(originOfTile.y()));
1917
1918         // Do we intersect the current tile or no?
1919         r.intersect(tile->rect());
1920         if (r.isEmpty())
1921             continue;
1922
1923         // If we do append to list and Voila!
1924         TileRect tileRect;
1925         tileRect.first = index;
1926         tileRect.second = r;
1927         tileRectList.append(tileRect);
1928     }
1929     return tileRectList;
1930 }
1931
1932 void BackingStorePrivate::updateTileMatrixIfNeeded()
1933 {
1934     // This will update the tile matrix.
1935     scrollBackingStore(0, 0);
1936 }
1937
1938 void BackingStorePrivate::contentsSizeChanged(const Platform::IntSize&)
1939 {
1940     updateTileMatrixIfNeeded();
1941 }
1942
1943 void BackingStorePrivate::scrollChanged(const Platform::IntPoint&)
1944 {
1945     // FIXME: Need to do anything here?
1946 }
1947
1948 void BackingStorePrivate::transformChanged()
1949 {
1950     if (!m_webPage->isVisible())
1951         return;
1952
1953     if (!isActive()) {
1954         m_renderQueue->reset();
1955         m_renderQueue->addToQueue(RenderQueue::VisibleZoom, visibleContentsRect());
1956         m_webPage->d->setShouldResetTilesWhenShown(true);
1957         return;
1958     }
1959
1960     BackingStoreGeometry* currentState = frontState();
1961     TileMap currentMap = currentState->tileMap();
1962
1963     bool hasCurrentVisibleZoomJob = m_renderQueue->hasCurrentVisibleZoomJob();
1964     bool isLoading = m_client->isLoading();
1965     if (isLoading) {
1966         if (!hasCurrentVisibleZoomJob)
1967             m_visibleTileBufferRect = visibleContentsRect(); // Cache this for blitVisibleContents.
1968
1969         // Add the currently visible tiles to the render queue as visible zoom jobs.
1970         TileRectList tileRectList = mapFromTransformedContentsToTiles(visibleContentsRect());
1971         for (size_t i = 0; i < tileRectList.size(); ++i) {
1972             TileRect tileRect = tileRectList[i];
1973             TileIndex index = tileRect.first;
1974             Platform::IntRect dirtyTileRect = tileRect.second;
1975             BackingStoreTile* tile = currentMap.get(index);
1976
1977             // Invalidate the whole rect.
1978             tileRect.second = this->tileRect();
1979             Platform::IntRect wholeRect = mapFromTilesToTransformedContents(tileRect);
1980             m_renderQueue->addToQueue(RenderQueue::VisibleZoom, wholeRect);
1981
1982             // Copy the visible contents into the visibleTileBuffer if we don't have
1983             // any current visible zoom jobs.
1984             if (!hasCurrentVisibleZoomJob) {
1985                 // Map to the destination's coordinate system.
1986                 Platform::IntPoint difference = this->originOfTile(index) - m_visibleTileBufferRect.location();
1987                 Platform::IntSize offset = Platform::IntSize(difference.x(), difference.y());
1988                 Platform::IntRect dirtyRect = dirtyTileRect;
1989                 dirtyRect.move(offset.width(), offset.height());
1990
1991                 BackingStoreTile* visibleTileBuffer
1992                     = SurfacePool::globalSurfacePool()->visibleTileBuffer();
1993                 ASSERT(visibleTileBuffer->size() == Platform::IntSize(m_webPage->d->transformedViewportSize()));
1994                 BlackBerry::Platform::Graphics::blitToBuffer(
1995                     visibleTileBuffer->frontBuffer()->nativeBuffer(), dirtyRect,
1996                     tile->frontBuffer()->nativeBuffer(), dirtyTileRect);
1997             }
1998         }
1999     }
2000
2001     m_renderQueue->reset();
2002     resetTiles(true /*resetBackground*/);
2003 }
2004
2005 void BackingStorePrivate::orientationChanged()
2006 {
2007     updateTileMatrixIfNeeded();
2008     createVisibleTileBuffer();
2009 }
2010
2011 void BackingStorePrivate::actualVisibleSizeChanged(const Platform::IntSize& size)
2012 {
2013 }
2014
2015 static void createVisibleTileBufferForWebPage(WebPagePrivate* page)
2016 {
2017     ASSERT(page);
2018     SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
2019     surfacePool->initializeVisibleTileBuffer(page->transformedViewportSize());
2020 }
2021
2022 void BackingStorePrivate::createSurfaces()
2023 {
2024     BackingStoreGeometry* currentState = frontState();
2025     TileMap currentMap = currentState->tileMap();
2026
2027     ASSERT(currentMap.isEmpty());
2028
2029     if (m_webPage->isVisible()) {
2030         // This method is only to be called as part of setting up a new web page instance and
2031         // before said instance is made visible so as to ensure a consistent definition of web
2032         // page visibility. That is, a web page is said to be visible when explicitly made visible.
2033         ASSERT_NOT_REACHED();
2034         return;
2035     }
2036
2037     SurfacePool* surfacePool = SurfacePool::globalSurfacePool();
2038     surfacePool->initialize(tileSize());
2039
2040     if (surfacePool->isEmpty()) // Settings specify 0 tiles / no backing store.
2041         return;
2042
2043     const Divisor divisor = bestDivisor(expandedContentsSize(), tileWidth(), tileHeight(), minimumNumberOfTilesWide(), minimumNumberOfTilesHigh(), m_preferredTileMatrixDimension);
2044
2045     int numberOfTilesWide = divisor.first;
2046     int numberOfTilesHigh = divisor.second;
2047
2048     const SurfacePool::TileList tileList = surfacePool->tileList();
2049     ASSERT(static_cast<int>(tileList.size()) >= (numberOfTilesWide * numberOfTilesHigh));
2050
2051     TileMap newTileMap;
2052     for (int y = 0; y < numberOfTilesHigh; ++y) {
2053         for (int x = 0; x < numberOfTilesWide; ++x) {
2054             TileIndex index(x, y);
2055             newTileMap.add(index, tileList.at(x + y * numberOfTilesWide));
2056         }
2057     }
2058
2059     // Set the initial state of the backingstore geometry.
2060     backState()->setNumberOfTilesWide(divisor.first);
2061     backState()->setNumberOfTilesHigh(divisor.second);
2062     backState()->setTileMap(newTileMap);
2063
2064     // Swap back/front state.
2065     swapState();
2066
2067     createVisibleTileBufferForWebPage(m_webPage->d);
2068 }
2069
2070 void BackingStorePrivate::createVisibleTileBuffer()
2071 {
2072     if (!m_webPage->isVisible() || !isActive())
2073         return;
2074
2075     createVisibleTileBufferForWebPage(m_webPage->d);
2076 }
2077
2078 Platform::IntPoint BackingStorePrivate::originOfTile(const TileIndex& index) const
2079 {
2080     return originOfTile(index, frontState()->backingStoreRect());
2081 }
2082
2083 Platform::IntPoint BackingStorePrivate::originOfTile(const TileIndex& index, const Platform::IntRect& backingStoreRect) const
2084 {
2085     return Platform::IntPoint(backingStoreRect.x() + (index.i() * tileWidth()),
2086                               backingStoreRect.y() + (index.j() * tileHeight()));
2087 }
2088
2089 int BackingStorePrivate::minimumNumberOfTilesWide() const
2090 {
2091     // The minimum number of tiles wide required to fill the viewport + 1 tile extra to allow scrolling.
2092     return static_cast<int>(ceilf(m_client->transformedViewportSize().width() / static_cast<float>(tileWidth()))) + 1;
2093 }
2094
2095 int BackingStorePrivate::minimumNumberOfTilesHigh() const
2096 {
2097     // The minimum number of tiles high required to fill the viewport + 1 tile extra to allow scrolling.
2098     return static_cast<int>(ceilf(m_client->transformedViewportSize().height() / static_cast<float>(tileHeight()))) + 1;
2099 }
2100
2101 Platform::IntSize BackingStorePrivate::expandedContentsSize() const
2102 {
2103     return m_client->transformedContentsSize().expandedTo(m_client->transformedViewportSize());
2104 }
2105
2106 int BackingStorePrivate::tileWidth()
2107 {
2108     static int tileWidth = BlackBerry::Platform::Graphics::Screen::primaryScreen()->landscapeWidth();
2109     return tileWidth;
2110 }
2111
2112 int BackingStorePrivate::tileHeight()
2113 {
2114     static int tileHeight = BlackBerry::Platform::Graphics::Screen::primaryScreen()->landscapeHeight();
2115     return tileHeight;
2116 }
2117
2118 Platform::IntSize BackingStorePrivate::tileSize()
2119 {
2120     return Platform::IntSize(tileWidth(), tileHeight());
2121 }
2122
2123 Platform::IntRect BackingStorePrivate::tileRect()
2124 {
2125     return Platform::IntRect(0, 0, tileWidth(), tileHeight());
2126 }
2127
2128 void BackingStorePrivate::renderContents(BlackBerry::Platform::Graphics::Buffer* tileBuffer,
2129                                          const Platform::IntPoint& surfaceOffset,
2130                                          const Platform::IntRect& contentsRect) const
2131 {
2132     // If tileBuffer == 0, we render directly to the window.
2133     if (!m_webPage->isVisible() && tileBuffer)
2134         return;
2135
2136 #if DEBUG_BACKINGSTORE
2137     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
2138                            "BackingStorePrivate::renderContents tileBuffer=0x%x surfaceOffset=(%d,%d) contentsRect=(%d,%d %dx%d)",
2139                            tileBuffer, surfaceOffset.x(), surfaceOffset.y(),
2140                            contentsRect.x(), contentsRect.y(), contentsRect.width(), contentsRect.height());
2141 #endif
2142
2143     // It is up to callers of this method to perform layout themselves!
2144     ASSERT(!m_webPage->d->mainFrame()->view()->needsLayout());
2145
2146     Platform::IntSize contentsSize = m_client->contentsSize();
2147     Color backgroundColor(m_webPage->settings()->backgroundColor());
2148
2149     BlackBerry::Platform::Graphics::Buffer* targetBuffer = tileBuffer
2150         ? tileBuffer
2151         : buffer();
2152
2153     if (contentsSize.isEmpty()
2154         || !Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedContentsSize()).contains(contentsRect)
2155         || backgroundColor.hasAlpha()) {
2156         // Clear the area if it's not fully covered by (opaque) contents.
2157         BlackBerry::Platform::IntRect clearRect = BlackBerry::Platform::IntRect(
2158             contentsRect.x() - surfaceOffset.x(), contentsRect.y() - surfaceOffset.y(),
2159             contentsRect.width(), contentsRect.height());
2160
2161         BlackBerry::Platform::Graphics::clearBuffer(targetBuffer, clearRect,
2162             backgroundColor.red(), backgroundColor.green(),
2163             backgroundColor.blue(), backgroundColor.alpha());
2164     }
2165
2166     if (contentsSize.isEmpty())
2167         return;
2168
2169 #if USE(ACCELERATED_COMPOSITING)
2170     // When committing the pending accelerated compositing layer changes, it's
2171     // necessary to draw the new layer appearance. This is normally done as
2172     // part of a blit, but if no blit happens because of this rendering, for
2173     // example because we're rendering an offscreen rectangle, someone needs to
2174     // catch this flag and make sure those layers get drawn.
2175     // This is just a complicated way to do
2176     // "if (commitRootLayerIfNeeded()) drawLayersOnCommit();"
2177     if (m_webPage->d->commitRootLayerIfNeeded())
2178         m_needsDrawLayersOnCommit = true;
2179 #endif
2180
2181     BlackBerry::Platform::Graphics::Drawable* bufferDrawable =
2182         BlackBerry::Platform::Graphics::lockBufferDrawable(targetBuffer);
2183
2184     PlatformGraphicsContext* bufferPlatformGraphicsContext = bufferDrawable
2185         ? SurfacePool::globalSurfacePool()->createPlatformGraphicsContext(bufferDrawable)
2186         : 0;
2187     PlatformGraphicsContext* targetPlatformGraphicsContext = bufferPlatformGraphicsContext
2188         ? bufferPlatformGraphicsContext
2189         : SurfacePool::globalSurfacePool()->lockTileRenderingSurface();
2190
2191     ASSERT(targetPlatformGraphicsContext);
2192
2193     {
2194         GraphicsContext graphicsContext(targetPlatformGraphicsContext);
2195
2196         // Believe it or not this is important since the WebKit Skia backend
2197         // doesn't store the original state unless you call save first :P
2198         graphicsContext.save();
2199
2200         // Translate context according to offset.
2201         graphicsContext.translate(-surfaceOffset.x(), -surfaceOffset.y());
2202
2203         // Add our transformation matrix as the global transform.
2204         AffineTransform affineTransform(
2205             m_webPage->d->transformationMatrix()->a(),
2206             m_webPage->d->transformationMatrix()->b(),
2207             m_webPage->d->transformationMatrix()->c(),
2208             m_webPage->d->transformationMatrix()->d(),
2209             m_webPage->d->transformationMatrix()->e(),
2210             m_webPage->d->transformationMatrix()->f());
2211         graphicsContext.concatCTM(affineTransform);
2212
2213         // Now that the matrix is applied we need untranformed contents coordinates.
2214         Platform::IntRect untransformedContentsRect = m_webPage->d->mapFromTransformed(contentsRect);
2215
2216         // We extract from the contentsRect but draw a slightly larger region than
2217         // we were told to, in order to avoid pixels being rendered only partially.
2218         const int atLeastOneDevicePixel =
2219             static_cast<int>(ceilf(1.0 / m_webPage->d->transformationMatrix()->a()));
2220         untransformedContentsRect.inflate(atLeastOneDevicePixel, atLeastOneDevicePixel);
2221
2222         // Make sure the untransformed rectangle for the (slightly larger than
2223         // initially requested) repainted region is within the bounds of the page.
2224         untransformedContentsRect.intersect(Platform::IntRect(Platform::IntPoint(0, 0), contentsSize));
2225
2226         // Some WebKit painting backends *cough* Skia *cough* don't set this automatically
2227         // to the dirtyRect so do so here explicitly.
2228         graphicsContext.clip(untransformedContentsRect);
2229
2230         // Take care of possible left overflow on RTL page.
2231         if (int leftOverFlow = m_client->frame()->view()->minimumScrollPosition().x()) {
2232             untransformedContentsRect.move(leftOverFlow, 0);
2233             graphicsContext.translate(-leftOverFlow, 0);
2234         }
2235
2236         // Let WebCore render the page contents into the drawing surface.
2237         m_client->frame()->view()->paintContents(&graphicsContext, untransformedContentsRect);
2238
2239 #if ENABLE(INSPECTOR)
2240         if (m_webPage->d->m_page->inspectorController()->enabled()) {
2241             WebCore::IntPoint scrollPosition = m_client->frame()->view()->scrollPosition();
2242             graphicsContext.translate(scrollPosition.x(), scrollPosition.y());
2243             m_webPage->d->m_page->inspectorController()->drawHighlight(graphicsContext);
2244         }
2245 #endif
2246
2247         graphicsContext.restore();
2248     }
2249
2250     // Grab the requested region from the drawing surface into the tile image.
2251
2252     delete bufferPlatformGraphicsContext;
2253
2254     if (bufferDrawable)
2255         releaseBufferDrawable(targetBuffer);
2256     else {
2257         const Platform::IntPoint dstPoint(contentsRect.x() - surfaceOffset.x(),
2258                                           contentsRect.y() - surfaceOffset.y());
2259         const Platform::IntRect dstRect(dstPoint, contentsRect.size());
2260         const Platform::IntRect srcRect = dstRect;
2261
2262         // If we couldn't directly draw to the buffer, copy from the drawing surface.
2263         SurfacePool::globalSurfacePool()->releaseTileRenderingSurface(targetPlatformGraphicsContext);
2264         BlackBerry::Platform::Graphics::blitToBuffer(targetBuffer, dstRect, BlackBerry::Platform::Graphics::drawingSurface(), srcRect);
2265     }
2266 }
2267
2268 #if DEBUG_FAT_FINGERS
2269 static void drawDebugRect(BlackBerry::Platform::Graphics::Buffer* dstBuffer, const Platform::IntRect& dstRect, const Platform::IntRect& srcRect, unsigned char red, unsigned char green, unsigned char blue)
2270 {
2271     Platform::IntRect drawRect(srcRect);
2272     drawRect.intersect(dstRect);
2273     if (!drawRect.isEmpty())
2274         BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, drawRect, red, green, blue, 128);
2275 }
2276 #endif
2277
2278 void BackingStorePrivate::blitToWindow(const Platform::IntRect& dstRect,
2279                                        const BlackBerry::Platform::Graphics::Buffer* srcBuffer,
2280                                        const Platform::IntRect& srcRect,
2281                                        bool blend,
2282                                        unsigned char globalAlpha)
2283 {
2284     ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
2285
2286     windowFrontBufferState()->clearBlittedRegion(dstRect);
2287     windowBackBufferState()->addBlittedRegion(dstRect);
2288
2289     BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
2290     ASSERT(dstBuffer);
2291     ASSERT(srcBuffer);
2292     if (!dstBuffer)
2293         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't blitToWindow");
2294
2295     BlackBerry::Platform::Graphics::BlendMode blendMode = blend
2296         ? BlackBerry::Platform::Graphics::SourceOver
2297         : BlackBerry::Platform::Graphics::SourceCopy;
2298
2299     BlackBerry::Platform::Graphics::blitToBuffer(dstBuffer, dstRect, srcBuffer, srcRect, blendMode, globalAlpha);
2300
2301 #if DEBUG_FAT_FINGERS
2302     drawDebugRect(dstBuffer, dstRect, FatFingers::m_debugFatFingerRect, 210, 210, 250);
2303     drawDebugRect(dstBuffer, dstRect, Platform::IntRect(FatFingers::m_debugFatFingerClickPosition, Platform::IntSize(3, 3)), 0, 0, 0);
2304     drawDebugRect(dstBuffer, dstRect, Platform::IntRect(FatFingers::m_debugFatFingerAdjustedPosition, Platform::IntSize(5, 5)), 100, 100, 100);
2305 #endif
2306
2307 }
2308
2309 void BackingStorePrivate::checkerWindow(const Platform::IntRect& dstRect,
2310                                         const Platform::IntPoint& contentsOrigin,
2311                                         double contentsScale)
2312 {
2313     ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
2314
2315     windowFrontBufferState()->clearBlittedRegion(dstRect);
2316     windowBackBufferState()->addBlittedRegion(dstRect);
2317
2318     BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
2319     ASSERT(dstBuffer);
2320     if (!dstBuffer)
2321         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't checkerWindow");
2322
2323     Color color(m_webPage->settings()->backgroundColor());
2324     unsigned char alpha = color.alpha();
2325     BlackBerry::Platform::Graphics::checkerBuffer(dstBuffer, dstRect, contentsOrigin, contentsScale, alpha);
2326 }
2327
2328 void BackingStorePrivate::invalidateWindow()
2329 {
2330     // Grab a rect appropriate for the current thread.
2331     if (BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
2332         invalidateWindow(m_webPage->client()->userInterfaceBlittedDestinationRect());
2333     else
2334         invalidateWindow(Platform::IntRect(Platform::IntPoint(0, 0), m_client->transformedViewportSize()));
2335 }
2336
2337 void BackingStorePrivate::invalidateWindow(const Platform::IntRect& dst)
2338 {
2339     if (dst.isEmpty())
2340         return;
2341
2342     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) {
2343         // This needs to be sync in order to swap the recently drawn thing...
2344         // This will only be called from WebKit thread during direct rendering.
2345         typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&);
2346         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchSyncMessage(
2347             BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate, Platform::IntRect>(
2348                 &BackingStorePrivate::invalidateWindow, this, dst));
2349         return;
2350     }
2351
2352 #if DEBUG_BACKINGSTORE
2353     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::invalidateWindow dst = %s", dst.toString().c_str());
2354 #endif
2355
2356     // Since our window may also be double buffered, we need to also copy the
2357     // front buffer's contents to the back buffer before we swap them. It is
2358     // analogous to what we do with our double buffered tiles by calling
2359     // copyPreviousContentsToBackingSurfaceOfTile(). It only affects partial
2360     // screen updates since when we are scrolling or zooming, the whole window
2361     // is invalidated anyways and no copying is needed.
2362     copyPreviousContentsToBackSurfaceOfWindow();
2363
2364     Platform::IntRect dstRect = dst;
2365
2366     Platform::IntRect viewportRect(Platform::IntPoint(0, 0), m_client->transformedViewportSize());
2367     dstRect.intersect(viewportRect);
2368
2369     if (dstRect.width() <= 0 || dstRect.height() <= 0)
2370         return;
2371
2372 #if DEBUG_BACKINGSTORE
2373     BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical, "BackingStorePrivate::invalidateWindow posting = %s", dstRect.toString().c_str());
2374 #endif
2375
2376     m_currentWindowBackBuffer = (m_currentWindowBackBuffer + 1) % 2;
2377     if (Window* window = m_webPage->client()->window())
2378         window->post(dstRect);
2379 }
2380
2381 void BackingStorePrivate::clearWindow()
2382 {
2383     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) {
2384         typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)();
2385         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
2386             BlackBerry::Platform::createMethodCallMessage<FunctionType, BackingStorePrivate>(
2387                 &BackingStorePrivate::clearWindow, this));
2388         return;
2389     }
2390
2391     BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
2392     ASSERT(dstBuffer);
2393     if (!dstBuffer)
2394         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't clearWindow");
2395
2396     windowFrontBufferState()->clearBlittedRegion();
2397     windowBackBufferState()->addBlittedRegion(Platform::IntRect(
2398         Platform::IntPoint(0, 0), surfaceSize()));
2399
2400     Color color(m_webPage->settings()->backgroundColor());
2401     BlackBerry::Platform::Graphics::clearBuffer(dstBuffer,
2402         color.red(), color.green(), color.blue(), color.alpha());
2403 }
2404
2405 void BackingStorePrivate::clearWindow(const Platform::IntRect& rect,
2406                                       unsigned char red,
2407                                       unsigned char green,
2408                                       unsigned char blue,
2409                                       unsigned char alpha)
2410 {
2411     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread() && !shouldDirectRenderingToWindow()) {
2412         typedef void (BlackBerry::WebKit::BackingStorePrivate::*FunctionType)(const Platform::IntRect&,
2413                                                                            unsigned char,
2414                                                                            unsigned char,
2415                                                                            unsigned char,
2416                                                                            unsigned char);
2417         BlackBerry::Platform::userInterfaceThreadMessageClient()->dispatchMessage(
2418             BlackBerry::Platform::createMethodCallMessage<FunctionType,
2419                                                        BackingStorePrivate,
2420                                                        Platform::IntRect,
2421                                                        unsigned char,
2422                                                        unsigned char,
2423                                                        unsigned char,
2424                                                        unsigned char>(
2425                 &BackingStorePrivate::clearWindow, this, rect, red, green, blue, alpha));
2426         return;
2427     }
2428
2429     BlackBerry::Platform::Graphics::Buffer* dstBuffer = buffer();
2430     ASSERT(dstBuffer);
2431     if (!dstBuffer)
2432         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelWarn, "Empty window buffer, couldn't clearWindow");
2433
2434     windowFrontBufferState()->clearBlittedRegion(rect);
2435     windowBackBufferState()->addBlittedRegion(rect);
2436
2437     BlackBerry::Platform::Graphics::clearBuffer(dstBuffer, rect, red, green, blue, alpha);
2438 }
2439
2440 bool BackingStorePrivate::isScrollingOrZooming() const
2441 {
2442     BackingStoreMutexLocker locker(const_cast<BackingStorePrivate*>(this));
2443     return m_isScrollingOrZooming;
2444 }
2445
2446 void BackingStorePrivate::setScrollingOrZooming(bool scrollingOrZooming, bool shouldBlit)
2447 {
2448     {
2449         BackingStoreMutexLocker locker(this);
2450         m_isScrollingOrZooming = scrollingOrZooming;
2451     }
2452
2453 #if !ENABLE_REPAINTONSCROLL
2454     m_suspendRenderJobs = scrollingOrZooming; // Suspend the rendering of everything.
2455 #endif
2456
2457     if (!m_webPage->settings()->shouldRenderAnimationsOnScrollOrZoom())
2458         m_suspendRegularRenderJobs = scrollingOrZooming; // Suspend the rendering of animations.
2459
2460     // Clear this flag since we don't care if the render queue is under pressure
2461     // or not since we are scrolling and it is more important to not lag than
2462     // it is to ensure animations achieve better framerates!
2463     if (scrollingOrZooming)
2464         m_renderQueue->setCurrentRegularRenderJobBatchUnderPressure(false);
2465 #if ENABLE_SCROLLBARS
2466     else if (shouldBlit && !shouldDirectRenderingToWindow())
2467         blitVisibleContents();
2468 #endif
2469 }
2470
2471 void BackingStorePrivate::lockBackingStore()
2472 {
2473     pthread_mutex_lock(&m_mutex);
2474 }
2475
2476 void BackingStorePrivate::unlockBackingStore()
2477 {
2478     pthread_mutex_unlock(&m_mutex);
2479 }
2480
2481 BackingStoreGeometry* BackingStorePrivate::frontState() const
2482 {
2483     return reinterpret_cast<BackingStoreGeometry*>(m_frontState);
2484 }
2485
2486 BackingStoreGeometry* BackingStorePrivate::backState() const
2487 {
2488     return reinterpret_cast<BackingStoreGeometry*>(m_backState);
2489 }
2490
2491 void BackingStorePrivate::swapState()
2492 {
2493     unsigned front = reinterpret_cast<unsigned>(frontState());
2494     unsigned back = reinterpret_cast<unsigned>(backState());
2495
2496     // Atomic change.
2497     _smp_xchg(&m_frontState, back);
2498     _smp_xchg(&m_backState, front);
2499     BlackBerry::Platform::userInterfaceThreadMessageClient()->syncToCurrentMessage();
2500 }
2501
2502 BackingStoreWindowBufferState* BackingStorePrivate::windowFrontBufferState() const
2503 {
2504     return &m_windowBufferState[(m_currentWindowBackBuffer + 1) % 2];
2505 }
2506
2507 BackingStoreWindowBufferState* BackingStorePrivate::windowBackBufferState() const
2508 {
2509     return &m_windowBufferState[m_currentWindowBackBuffer];
2510 }
2511
2512 #if USE(ACCELERATED_COMPOSITING)
2513 bool BackingStorePrivate::drawSubLayers()
2514 {
2515     ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
2516     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
2517         return false;
2518
2519     if (m_suspendBackingStoreUpdates && !isOpenGLCompositing())
2520         return false;
2521
2522     Platform::IntRect dst = m_webPage->client()->userInterfaceBlittedDestinationRect();
2523     if (dst.isEmpty())
2524         return false;
2525
2526     Platform::IntRect src = m_webPage->client()->userInterfaceBlittedVisibleContentsRect();
2527     WebCore::FloatRect contentsRect = m_webPage->d->mapFromTransformedFloatRect(
2528         WebCore::FloatRect(WebCore::IntRect(src)));
2529     return m_webPage->d->drawSubLayers(dst, contentsRect);
2530 }
2531
2532 bool BackingStorePrivate::drawLayersOnCommitIfNeeded()
2533 {
2534     // Check if rendering caused a commit and we need to redraw the layers
2535     if (!m_needsDrawLayersOnCommit)
2536         return false;
2537
2538     m_needsDrawLayersOnCommit = false;
2539     m_webPage->d->drawLayersOnCommit();
2540
2541     return true;
2542 }
2543
2544 void BackingStorePrivate::drawAndBlendLayersForDirectRendering(const Platform::IntRect& dirtyRect)
2545 {
2546     ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
2547     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
2548         return;
2549
2550     // Because we're being called sync from the WebKit thread, we can use
2551     // regular WebPage size and transformation functions without concerns.
2552     WebCore::IntRect contentsRect = visibleContentsRect();
2553     WebCore::FloatRect untransformedContentsRect = m_webPage->d->mapFromTransformedFloatRect(WebCore::FloatRect(contentsRect));
2554     WebCore::IntRect contentsScreenRect = m_client->mapFromTransformedContentsToTransformedViewport(contentsRect);
2555     WebCore::IntRect dstRect = intersection(contentsScreenRect,
2556         WebCore::IntRect(WebCore::IntPoint(0, 0), m_webPage->d->transformedViewportSize()));
2557
2558     // Check if rendering caused a commit and we need to redraw the layers.
2559     m_needsDrawLayersOnCommit = false;
2560     m_webPage->d->drawSubLayers(dstRect, untransformedContentsRect);
2561
2562 #if ENABLE_COMPOSITING_SURFACE
2563     // See above comment about sync calling, visibleContentsRect() is safe here.
2564     Platform::IntRect visibleDirtyRect = dirtyRect;
2565     visibleDirtyRect.intersect(visibleContentsRect());
2566     visibleDirtyRect = m_client->mapFromTransformedContentsToTransformedViewport(visibleDirtyRect);
2567
2568     blendCompositingSurface(visibleDirtyRect);
2569 #endif
2570 }
2571
2572 bool BackingStorePrivate::drawLayersOnCommitIfNeeded()
2573 {
2574     // Check if rendering caused a commit and we need to redraw the layers
2575     if (!m_needsDrawLayersOnCommit)
2576         return false;
2577
2578     m_needsDrawLayersOnCommit = false;
2579     m_webPage->d->drawLayersOnCommit();
2580
2581     return true;
2582 }
2583
2584 void BackingStorePrivate::drawAndBlendLayersForDirectRendering(const Platform::IntRect& dirtyRect)
2585 {
2586     ASSERT(BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread());
2587     if (!BlackBerry::Platform::userInterfaceThreadMessageClient()->isCurrentThread())
2588         return;
2589
2590     // Because we're being called sync from the WebKit thread, we can use
2591     // regular WebPage size and transformation functions without concerns.
2592     WebCore::IntRect contentsRect = visibleContentsRect();
2593     WebCore::FloatRect untransformedContentsRect = m_webPage->d->mapFromTransformedFloatRect(WebCore::FloatRect(contentsRect));
2594     WebCore::IntRect contentsScreenRect = m_client->mapFromTransformedContentsToTransformedViewport(contentsRect);
2595     WebCore::IntRect dstRect = intersection(contentsScreenRect,
2596         WebCore::IntRect(WebCore::IntPoint(0, 0), m_webPage->d->transformedViewportSize()));
2597
2598     // Check if rendering caused a commit and we need to redraw the layers.
2599     m_needsDrawLayersOnCommit = false;
2600     m_webPage->d->drawSubLayers(dstRect, untransformedContentsRect);
2601
2602 #if ENABLE_COMPOSITING_SURFACE
2603     // See above comment about sync calling, visibleContentsRect() is safe here.
2604     Platform::IntRect visibleDirtyRect = dirtyRect;
2605     visibleDirtyRect.intersect(visibleContentsRect());
2606     visibleDirtyRect = m_client->mapFromTransformedContentsToTransformedViewport(visibleDirtyRect);
2607
2608     blendCompositingSurface(visibleDirtyRect);
2609 #endif
2610 }
2611 #endif
2612
2613 bool BackingStorePrivate::isActive() const
2614 {
2615     return BackingStorePrivate::s_currentBackingStoreOwner == m_webPage && SurfacePool::globalSurfacePool()->isActive();
2616 }
2617
2618 BackingStore::BackingStore(WebPage* webPage, BackingStoreClient* client)
2619     : d(new BackingStorePrivate)
2620 {
2621     d->m_webPage = webPage;
2622     d->m_client = client;
2623 }
2624
2625 BackingStore::~BackingStore()
2626 {
2627     delete d;
2628     d = 0;
2629 }
2630
2631 void BackingStore::createSurface()
2632 {
2633     static bool initialized = false;
2634     if (!initialized) {
2635         BlackBerry::Platform::Graphics::initialize();
2636         initialized = true;
2637     }
2638
2639     // Triggers creation of surfaces in backingstore.
2640     d->createSurfaces();
2641
2642     // Focusing the WebPage triggers a repaint, so while we want it to be
2643     // focused initially this has to happen after creation of the surface.
2644     d->m_webPage->setFocused(true);
2645 }
2646
2647 void BackingStore::suspendScreenAndBackingStoreUpdates()
2648 {
2649     d->suspendScreenAndBackingStoreUpdates();
2650 }
2651
2652 void BackingStore::resumeScreenAndBackingStoreUpdates(ResumeUpdateOperation op)
2653 {
2654     d->resumeScreenAndBackingStoreUpdates(op);
2655 }
2656
2657 bool BackingStore::isScrollingOrZooming() const
2658 {
2659     return d->isScrollingOrZooming();
2660 }
2661
2662 void BackingStore::setScrollingOrZooming(bool scrollingOrZooming)
2663 {
2664     d->setScrollingOrZooming(scrollingOrZooming);
2665 }
2666
2667 void BackingStore::blitContents(const BlackBerry::Platform::IntRect& dstRect, const BlackBerry::Platform::IntRect& contents)
2668 {
2669     // Blitting during direct rendering is not supported.
2670     if (isDirectRenderingToWindow()) {
2671         BlackBerry::Platform::log(BlackBerry::Platform::LogLevelCritical,
2672                                "BackingStore::blitContents operation not supported in direct rendering mode");
2673         return;
2674     }
2675
2676     d->blitContents(dstRect, contents);
2677 }
2678
2679 void BackingStore::repaint(int x, int y, int width, int height,
2680                            bool contentChanged, bool immediate)
2681 {
2682     d->repaint(Platform::IntRect(x, y, width, height), contentChanged, immediate);
2683 }
2684
2685 bool BackingStore::hasRenderJobs() const
2686 {
2687     return d->shouldPerformRenderJobs();
2688 }
2689
2690 void BackingStore::renderOnIdle()
2691 {
2692     d->renderOnIdle();
2693 }
2694
2695 bool BackingStore::isDirectRenderingToWindow() const
2696 {
2697     BackingStoreMutexLocker locker(d);
2698     return d->shouldDirectRenderingToWindow();
2699 }
2700
2701 void BackingStore::createBackingStoreMemory()
2702 {
2703     SurfacePool::globalSurfacePool()->createBuffers();
2704 }
2705
2706 void BackingStore::releaseBackingStoreMemory()
2707 {
2708     SurfacePool::globalSurfacePool()->releaseBuffers();
2709 }
2710
2711 bool BackingStore::defersBlit() const
2712 {
2713         return d->m_defersBlit;
2714 }
2715
2716 void BackingStore::setDefersBlit(bool b)
2717 {
2718         d->m_defersBlit = b;
2719 }
2720
2721 bool BackingStore::hasBlitJobs() const
2722 {
2723     // Normally, this would be called from the compositing thread,
2724     // and the flag is set on the compositing thread, so no need for
2725     // synchronization.
2726     return d->m_hasBlitJobs;
2727 }
2728
2729 void BackingStore::blitOnIdle()
2730 {
2731     d->blitVisibleContents(true /*force*/);
2732 }
2733
2734 Platform::IntSize BackingStorePrivate::surfaceSize() const
2735 {
2736     if (Window* window = m_webPage->client()->window())
2737         return window->surfaceSize();
2738
2739     return Platform::IntSize();
2740 }
2741
2742 Platform::Graphics::Buffer* BackingStorePrivate::buffer() const
2743 {
2744     if (Window* window = m_webPage->client()->window())
2745         return window->buffer();
2746
2747     return 0;
2748 }
2749
2750 }
2751 }