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