1/* 2 * Copyright (C) 2014 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#import "config.h" 27#import "ViewSnapshotStore.h" 28 29#import "WebBackForwardList.h" 30#import "WebPageProxy.h" 31#import <CoreGraphics/CoreGraphics.h> 32#import <WebCore/IOSurface.h> 33 34#if PLATFORM(IOS) 35#import <QuartzCore/QuartzCorePrivate.h> 36#endif 37 38using namespace WebCore; 39 40#if USE_IOSURFACE_VIEW_SNAPSHOTS 41static const size_t maximumSnapshotCacheSize = 400 * (1024 * 1024); 42#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 43// Because render server snapshots are not purgeable, we should keep fewer around. 44static const size_t maximumSnapshotCacheSize = 50 * (1024 * 1024); 45#endif 46 47namespace WebKit { 48 49ViewSnapshotStore::ViewSnapshotStore() 50 : m_snapshotCacheSize(0) 51{ 52} 53 54ViewSnapshotStore::~ViewSnapshotStore() 55{ 56 discardSnapshotImages(); 57} 58 59ViewSnapshotStore& ViewSnapshotStore::shared() 60{ 61 static ViewSnapshotStore& store = *new ViewSnapshotStore; 62 return store; 63} 64 65#if USE_RENDER_SERVER_VIEW_SNAPSHOTS 66CAContext *ViewSnapshotStore::snapshottingContext() 67{ 68 static CAContext *context; 69 static dispatch_once_t onceToken; 70 dispatch_once(&onceToken, ^{ 71 NSDictionary *options = @{ 72 kCAContextDisplayName: @"WebKitSnapshotting", 73 kCAContextIgnoresHitTest: @YES, 74 kCAContextDisplayId : @20000 75 }; 76 context = [[CAContext remoteContextWithOptions:options] retain]; 77 }); 78 79 return context; 80} 81#endif 82 83void ViewSnapshotStore::didAddImageToSnapshot(ViewSnapshot& snapshot) 84{ 85 bool isNewEntry = m_snapshotsWithImages.add(&snapshot).isNewEntry; 86 ASSERT_UNUSED(isNewEntry, isNewEntry); 87 m_snapshotCacheSize += snapshot.imageSizeInBytes(); 88} 89 90void ViewSnapshotStore::willRemoveImageFromSnapshot(ViewSnapshot& snapshot) 91{ 92 bool removed = m_snapshotsWithImages.remove(&snapshot); 93 ASSERT_UNUSED(removed, removed); 94 m_snapshotCacheSize -= snapshot.imageSizeInBytes(); 95} 96 97void ViewSnapshotStore::pruneSnapshots(WebPageProxy& webPageProxy) 98{ 99 if (m_snapshotCacheSize <= maximumSnapshotCacheSize) 100 return; 101 102 ASSERT(!m_snapshotsWithImages.isEmpty()); 103 104 // FIXME: We have enough information to do smarter-than-LRU eviction (making use of the back-forward lists, etc.) 105 106 m_snapshotsWithImages.first()->clearImage(); 107} 108 109void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy) 110{ 111 if (webPageProxy.isShowingNavigationGestureSnapshot()) 112 return; 113 114 WebBackForwardListItem* item = webPageProxy.backForwardList().currentItem(); 115 116 if (!item) 117 return; 118 119 pruneSnapshots(webPageProxy); 120 121 webPageProxy.willRecordNavigationSnapshot(*item); 122 123 RefPtr<ViewSnapshot> snapshot = webPageProxy.takeViewSnapshot(); 124 if (!snapshot || !snapshot->hasImage()) 125 return; 126 127 snapshot->setRenderTreeSize(webPageProxy.renderTreeSize()); 128 snapshot->setDeviceScaleFactor(webPageProxy.deviceScaleFactor()); 129 snapshot->setBackgroundColor(webPageProxy.pageExtendedBackgroundColor()); 130 131 item->setSnapshot(snapshot.release()); 132} 133 134void ViewSnapshotStore::discardSnapshotImages() 135{ 136 while (!m_snapshotsWithImages.isEmpty()) 137 m_snapshotsWithImages.first()->clearImage(); 138} 139 140 141#if USE_IOSURFACE_VIEW_SNAPSHOTS 142PassRefPtr<ViewSnapshot> ViewSnapshot::create(IOSurface* surface, IntSize size, size_t imageSizeInBytes) 143{ 144 return adoptRef(new ViewSnapshot(surface, size, imageSizeInBytes)); 145} 146#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 147PassRefPtr<ViewSnapshot> ViewSnapshot::create(uint32_t slotID, IntSize size, size_t imageSizeInBytes) 148{ 149 return adoptRef(new ViewSnapshot(slotID, size, imageSizeInBytes)); 150} 151#endif 152 153#if USE_IOSURFACE_VIEW_SNAPSHOTS 154ViewSnapshot::ViewSnapshot(IOSurface* surface, IntSize size, size_t imageSizeInBytes) 155 : m_surface(surface) 156#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 157ViewSnapshot::ViewSnapshot(uint32_t slotID, IntSize size, size_t imageSizeInBytes) 158 : m_slotID(slotID) 159#endif 160 , m_imageSizeInBytes(imageSizeInBytes) 161 , m_size(size) 162{ 163 if (hasImage()) 164 ViewSnapshotStore::shared().didAddImageToSnapshot(*this); 165} 166 167ViewSnapshot::~ViewSnapshot() 168{ 169 clearImage(); 170} 171 172bool ViewSnapshot::hasImage() const 173{ 174#if USE_IOSURFACE_VIEW_SNAPSHOTS 175 return m_surface; 176#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 177 return m_slotID; 178#endif 179} 180 181void ViewSnapshot::clearImage() 182{ 183 if (!hasImage()) 184 return; 185 186 ViewSnapshotStore::shared().willRemoveImageFromSnapshot(*this); 187 188#if USE_IOSURFACE_VIEW_SNAPSHOTS 189 m_surface = nullptr; 190#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 191 [ViewSnapshotStore::snapshottingContext() deleteSlot:m_slotID]; 192 m_slotID = 0; 193#endif 194 m_imageSizeInBytes = 0; 195} 196 197id ViewSnapshot::asLayerContents() 198{ 199#if USE_IOSURFACE_VIEW_SNAPSHOTS 200 if (!m_surface) 201 return nullptr; 202 203 if (m_surface->setIsVolatile(false) != IOSurface::SurfaceState::Valid) { 204 clearImage(); 205 return nullptr; 206 } 207 208 return (id)m_surface->surface(); 209#elif USE_RENDER_SERVER_VIEW_SNAPSHOTS 210 return [CAContext objectForSlot:m_slotID]; 211#endif 212} 213 214} // namespace WebKit 215