Home | History | Annotate | Download | only in UIProcess
      1 /*
      2  * Copyright (C) 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "DrawingAreaProxyImpl.h"
     28 
     29 #include "DrawingAreaMessages.h"
     30 #include "DrawingAreaProxyMessages.h"
     31 #include "LayerTreeContext.h"
     32 #include "Region.h"
     33 #include "UpdateInfo.h"
     34 #include "WebPageProxy.h"
     35 #include "WebProcessProxy.h"
     36 
     37 #if !PLATFORM(MAC) && !PLATFORM(WIN)
     38 #error "This drawing area is not ready for use by other ports yet."
     39 #endif
     40 
     41 using namespace WebCore;
     42 
     43 namespace WebKit {
     44 
     45 PassOwnPtr<DrawingAreaProxyImpl> DrawingAreaProxyImpl::create(WebPageProxy* webPageProxy)
     46 {
     47     return adoptPtr(new DrawingAreaProxyImpl(webPageProxy));
     48 }
     49 
     50 DrawingAreaProxyImpl::DrawingAreaProxyImpl(WebPageProxy* webPageProxy)
     51     : DrawingAreaProxy(DrawingAreaTypeImpl, webPageProxy)
     52     , m_currentBackingStoreStateID(0)
     53     , m_nextBackingStoreStateID(0)
     54     , m_isWaitingForDidUpdateBackingStoreState(false)
     55     , m_isBackingStoreDiscardable(true)
     56     , m_discardBackingStoreTimer(RunLoop::current(), this, &DrawingAreaProxyImpl::discardBackingStore)
     57 {
     58 }
     59 
     60 DrawingAreaProxyImpl::~DrawingAreaProxyImpl()
     61 {
     62 #if USE(ACCELERATED_COMPOSITING)
     63     // Make sure to exit accelerated compositing mode.
     64     if (isInAcceleratedCompositingMode())
     65         exitAcceleratedCompositingMode();
     66 #endif
     67 }
     68 
     69 void DrawingAreaProxyImpl::paint(BackingStore::PlatformGraphicsContext context, const IntRect& rect, Region& unpaintedRegion)
     70 {
     71     unpaintedRegion = rect;
     72 
     73     if (isInAcceleratedCompositingMode())
     74         return;
     75 
     76     ASSERT(m_currentBackingStoreStateID <= m_nextBackingStoreStateID);
     77     if (m_currentBackingStoreStateID < m_nextBackingStoreStateID) {
     78         // Tell the web process to do a full backing store update now, in case we previously told
     79         // it about our next state but didn't request an immediate update.
     80         sendUpdateBackingStoreState(RespondImmediately);
     81 
     82         if (m_isWaitingForDidUpdateBackingStoreState) {
     83             // Wait for a DidUpdateBackingStoreState message that contains the new bits before we paint
     84             // what's currently in the backing store.
     85             waitForAndDispatchDidUpdateBackingStoreState();
     86         }
     87 
     88         // Dispatching DidUpdateBackingStoreState (either beneath sendUpdateBackingStoreState or
     89         // beneath waitForAndDispatchDidUpdateBackingStoreState) could destroy our backing store or
     90         // change the compositing mode.
     91         if (!m_backingStore || isInAcceleratedCompositingMode())
     92             return;
     93     } else {
     94         ASSERT(!m_isWaitingForDidUpdateBackingStoreState);
     95         if (!m_backingStore) {
     96             // The view has asked us to paint before the web process has painted anything. There's
     97             // nothing we can do.
     98             return;
     99         }
    100     }
    101 
    102     m_backingStore->paint(context, rect);
    103     unpaintedRegion.subtract(IntRect(IntPoint(), m_backingStore->size()));
    104 
    105     discardBackingStoreSoon();
    106 }
    107 
    108 void DrawingAreaProxyImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*)
    109 {
    110     ASSERT_NOT_REACHED();
    111 }
    112 
    113 void DrawingAreaProxyImpl::didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*)
    114 {
    115     ASSERT_NOT_REACHED();
    116 }
    117 
    118 bool DrawingAreaProxyImpl::paint(const WebCore::IntRect&, PlatformDrawingContext)
    119 {
    120     ASSERT_NOT_REACHED();
    121     return false;
    122 }
    123 
    124 void DrawingAreaProxyImpl::sizeDidChange()
    125 {
    126     backingStoreStateDidChange(RespondImmediately);
    127 }
    128 
    129 void DrawingAreaProxyImpl::visibilityDidChange()
    130 {
    131     if (!m_webPageProxy->isViewVisible()) {
    132         // Suspend painting.
    133         m_webPageProxy->process()->send(Messages::DrawingArea::SuspendPainting(), m_webPageProxy->pageID());
    134         return;
    135     }
    136 
    137     // Resume painting.
    138     m_webPageProxy->process()->send(Messages::DrawingArea::ResumePainting(), m_webPageProxy->pageID());
    139 
    140 #if USE(ACCELERATED_COMPOSITING)
    141     // If we don't have a backing store, go ahead and mark the backing store as being changed so
    142     // that when paint we'll actually wait for something to paint and not flash white.
    143     if (!m_backingStore && m_layerTreeContext.isEmpty())
    144         backingStoreStateDidChange(DoNotRespondImmediately);
    145 #endif
    146 }
    147 
    148 void DrawingAreaProxyImpl::setPageIsVisible(bool)
    149 {
    150 }
    151 
    152 void DrawingAreaProxyImpl::setBackingStoreIsDiscardable(bool isBackingStoreDiscardable)
    153 {
    154     if (m_isBackingStoreDiscardable == isBackingStoreDiscardable)
    155         return;
    156 
    157     m_isBackingStoreDiscardable = isBackingStoreDiscardable;
    158     if (m_isBackingStoreDiscardable)
    159         discardBackingStoreSoon();
    160     else
    161         m_discardBackingStoreTimer.stop();
    162 }
    163 
    164 void DrawingAreaProxyImpl::update(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
    165 {
    166     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
    167     if (backingStoreStateID < m_currentBackingStoreStateID)
    168         return;
    169 
    170     // FIXME: Handle the case where the view is hidden.
    171 
    172     incorporateUpdate(updateInfo);
    173     m_webPageProxy->process()->send(Messages::DrawingArea::DidUpdate(), m_webPageProxy->pageID());
    174 }
    175 
    176 void DrawingAreaProxyImpl::didUpdateBackingStoreState(uint64_t backingStoreStateID, const UpdateInfo& updateInfo, const LayerTreeContext& layerTreeContext)
    177 {
    178     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_nextBackingStoreStateID);
    179     ASSERT_ARG(backingStoreStateID, backingStoreStateID > m_currentBackingStoreStateID);
    180     m_currentBackingStoreStateID = backingStoreStateID;
    181 
    182     m_isWaitingForDidUpdateBackingStoreState = false;
    183 
    184     if (m_nextBackingStoreStateID != m_currentBackingStoreStateID)
    185         sendUpdateBackingStoreState(RespondImmediately);
    186 
    187 #if USE(ACCELERATED_COMPOSITING)
    188     if (layerTreeContext != m_layerTreeContext) {
    189         if (!m_layerTreeContext.isEmpty()) {
    190             exitAcceleratedCompositingMode();
    191             ASSERT(m_layerTreeContext.isEmpty());
    192         }
    193 
    194         if (!layerTreeContext.isEmpty()) {
    195             enterAcceleratedCompositingMode(layerTreeContext);
    196             ASSERT(layerTreeContext == m_layerTreeContext);
    197         }
    198     }
    199 
    200     if (isInAcceleratedCompositingMode()) {
    201         ASSERT(!m_backingStore);
    202         return;
    203     }
    204 #endif
    205 
    206     // FIXME: We could just reuse our existing backing store if it's the same size as
    207     // updateInfo.viewSize.
    208     m_backingStore = nullptr;
    209     incorporateUpdate(updateInfo);
    210 }
    211 
    212 void DrawingAreaProxyImpl::enterAcceleratedCompositingMode(uint64_t backingStoreStateID, const LayerTreeContext& layerTreeContext)
    213 {
    214     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
    215     if (backingStoreStateID < m_currentBackingStoreStateID)
    216         return;
    217 
    218 #if USE(ACCELERATED_COMPOSITING)
    219     enterAcceleratedCompositingMode(layerTreeContext);
    220 #endif
    221 }
    222 
    223 void DrawingAreaProxyImpl::exitAcceleratedCompositingMode(uint64_t backingStoreStateID, const UpdateInfo& updateInfo)
    224 {
    225     ASSERT_ARG(backingStoreStateID, backingStoreStateID <= m_currentBackingStoreStateID);
    226     if (backingStoreStateID < m_currentBackingStoreStateID)
    227         return;
    228 
    229 #if USE(ACCELERATED_COMPOSITING)
    230     exitAcceleratedCompositingMode();
    231 #endif
    232 
    233     incorporateUpdate(updateInfo);
    234 }
    235 
    236 void DrawingAreaProxyImpl::incorporateUpdate(const UpdateInfo& updateInfo)
    237 {
    238     ASSERT(!isInAcceleratedCompositingMode());
    239 
    240     if (updateInfo.updateRectBounds.isEmpty())
    241         return;
    242 
    243     if (!m_backingStore)
    244         m_backingStore = BackingStore::create(updateInfo.viewSize, m_webPageProxy);
    245 
    246     m_backingStore->incorporateUpdate(updateInfo);
    247 
    248     bool shouldScroll = !updateInfo.scrollRect.isEmpty();
    249 
    250     if (shouldScroll)
    251         m_webPageProxy->scrollView(updateInfo.scrollRect, updateInfo.scrollOffset);
    252 
    253     for (size_t i = 0; i < updateInfo.updateRects.size(); ++i)
    254         m_webPageProxy->setViewNeedsDisplay(updateInfo.updateRects[i]);
    255 
    256     if (WebPageProxy::debugPaintFlags() & kWKDebugFlashBackingStoreUpdates)
    257         m_webPageProxy->flashBackingStoreUpdates(updateInfo.updateRects);
    258 
    259     if (shouldScroll)
    260         m_webPageProxy->displayView();
    261 }
    262 
    263 void DrawingAreaProxyImpl::backingStoreStateDidChange(RespondImmediatelyOrNot respondImmediatelyOrNot)
    264 {
    265     ++m_nextBackingStoreStateID;
    266     sendUpdateBackingStoreState(respondImmediatelyOrNot);
    267 }
    268 
    269 void DrawingAreaProxyImpl::sendUpdateBackingStoreState(RespondImmediatelyOrNot respondImmediatelyOrNot)
    270 {
    271     ASSERT(m_currentBackingStoreStateID < m_nextBackingStoreStateID);
    272 
    273     if (!m_webPageProxy->isValid())
    274         return;
    275 
    276     if (m_isWaitingForDidUpdateBackingStoreState)
    277         return;
    278 
    279     m_isWaitingForDidUpdateBackingStoreState = respondImmediatelyOrNot == RespondImmediately;
    280     m_webPageProxy->process()->send(Messages::DrawingArea::UpdateBackingStoreState(m_nextBackingStoreStateID, respondImmediatelyOrNot == RespondImmediately, m_size, m_scrollOffset), m_webPageProxy->pageID());
    281     m_scrollOffset = IntSize();
    282 
    283 #if USE(ACCELERATED_COMPOSITING)
    284     if (m_isWaitingForDidUpdateBackingStoreState && !m_layerTreeContext.isEmpty()) {
    285         // Wait for the DidUpdateBackingStoreState message. Normally we don this in DrawingAreaProxyImpl::paint, but that
    286         // function is never called when in accelerated compositing mode.
    287         waitForAndDispatchDidUpdateBackingStoreState();
    288     }
    289 #endif
    290 }
    291 
    292 void DrawingAreaProxyImpl::waitForAndDispatchDidUpdateBackingStoreState()
    293 {
    294     ASSERT(m_isWaitingForDidUpdateBackingStoreState);
    295 
    296     if (!m_webPageProxy->isValid())
    297         return;
    298     if (m_webPageProxy->process()->isLaunching())
    299         return;
    300 
    301 #if USE(ACCELERATED_COMPOSITING)
    302     // FIXME: waitForAndDispatchImmediately will always return the oldest DidUpdateBackingStoreState message that
    303     // hasn't yet been processed. But it might be better to skip ahead to some other DidUpdateBackingStoreState
    304     // message, if multiple DidUpdateBackingStoreState messages are waiting to be processed. For instance, we could
    305     // choose the most recent one, or the one that is closest to our current size.
    306 
    307     // The timeout, in seconds, we use when waiting for a DidUpdateBackingStoreState message when we're asked to paint.
    308     static const double didUpdateBackingStoreStateTimeout = 0.5;
    309     m_webPageProxy->process()->connection()->waitForAndDispatchImmediately<Messages::DrawingAreaProxy::DidUpdateBackingStoreState>(m_webPageProxy->pageID(), didUpdateBackingStoreStateTimeout);
    310 #endif
    311 }
    312 
    313 #if USE(ACCELERATED_COMPOSITING)
    314 void DrawingAreaProxyImpl::enterAcceleratedCompositingMode(const LayerTreeContext& layerTreeContext)
    315 {
    316     ASSERT(!isInAcceleratedCompositingMode());
    317 
    318     m_backingStore = nullptr;
    319     m_layerTreeContext = layerTreeContext;
    320     m_webPageProxy->enterAcceleratedCompositingMode(layerTreeContext);
    321 }
    322 
    323 void DrawingAreaProxyImpl::exitAcceleratedCompositingMode()
    324 {
    325     ASSERT(isInAcceleratedCompositingMode());
    326 
    327     m_layerTreeContext = LayerTreeContext();
    328     m_webPageProxy->exitAcceleratedCompositingMode();
    329 }
    330 #endif
    331 
    332 void DrawingAreaProxyImpl::discardBackingStoreSoon()
    333 {
    334     if (!m_isBackingStoreDiscardable)
    335         return;
    336 
    337     // We'll wait this many seconds after the last paint before throwing away our backing store to save memory.
    338     // FIXME: It would be smarter to make this delay based on how expensive painting is. See <http://webkit.org/b/55733>.
    339     static const double discardBackingStoreDelay = 5;
    340 
    341     m_discardBackingStoreTimer.startOneShot(discardBackingStoreDelay);
    342 }
    343 
    344 void DrawingAreaProxyImpl::discardBackingStore()
    345 {
    346     m_backingStore = nullptr;
    347     backingStoreStateDidChange(DoNotRespondImmediately);
    348 }
    349 
    350 } // namespace WebKit
    351