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