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 41using namespace WebCore; 42 43namespace WebKit { 44 45PassOwnPtr<DrawingAreaProxyImpl> DrawingAreaProxyImpl::create(WebPageProxy* webPageProxy) 46{ 47 return adoptPtr(new DrawingAreaProxyImpl(webPageProxy)); 48} 49 50DrawingAreaProxyImpl::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 60DrawingAreaProxyImpl::~DrawingAreaProxyImpl() 61{ 62#if USE(ACCELERATED_COMPOSITING) 63 // Make sure to exit accelerated compositing mode. 64 if (isInAcceleratedCompositingMode()) 65 exitAcceleratedCompositingMode(); 66#endif 67} 68 69void 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 108void DrawingAreaProxyImpl::didReceiveMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*) 109{ 110 ASSERT_NOT_REACHED(); 111} 112 113void DrawingAreaProxyImpl::didReceiveSyncMessage(CoreIPC::Connection*, CoreIPC::MessageID, CoreIPC::ArgumentDecoder*, CoreIPC::ArgumentEncoder*) 114{ 115 ASSERT_NOT_REACHED(); 116} 117 118bool DrawingAreaProxyImpl::paint(const WebCore::IntRect&, PlatformDrawingContext) 119{ 120 ASSERT_NOT_REACHED(); 121 return false; 122} 123 124void DrawingAreaProxyImpl::sizeDidChange() 125{ 126 backingStoreStateDidChange(RespondImmediately); 127} 128 129void 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 148void DrawingAreaProxyImpl::setPageIsVisible(bool) 149{ 150} 151 152void 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 164void 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 176void 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 212void 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 223void 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 236void 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 263void DrawingAreaProxyImpl::backingStoreStateDidChange(RespondImmediatelyOrNot respondImmediatelyOrNot) 264{ 265 ++m_nextBackingStoreStateID; 266 sendUpdateBackingStoreState(respondImmediatelyOrNot); 267} 268 269void 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 292void 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) 314void 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 323void DrawingAreaProxyImpl::exitAcceleratedCompositingMode() 324{ 325 ASSERT(isInAcceleratedCompositingMode()); 326 327 m_layerTreeContext = LayerTreeContext(); 328 m_webPageProxy->exitAcceleratedCompositingMode(); 329} 330#endif 331 332void 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 344void DrawingAreaProxyImpl::discardBackingStore() 345{ 346 m_backingStore = nullptr; 347 backingStoreStateDidChange(DoNotRespondImmediately); 348} 349 350} // namespace WebKit 351