35d1f3548b1e9c6e716ce589a3bc3993e1d63091
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / ViewSnapshotStore.mm
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 <WebCore/QuartzCoreSPI.h>
36 #endif
37
38 using namespace WebCore;
39
40 #if USE_IOSURFACE_VIEW_SNAPSHOTS
41 static 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.
44 static const size_t maximumSnapshotCacheSize = 50 * (1024 * 1024);
45 #endif
46
47 namespace WebKit {
48
49 ViewSnapshotStore::ViewSnapshotStore()
50     : m_snapshotCacheSize(0)
51 {
52 }
53
54 ViewSnapshotStore::~ViewSnapshotStore()
55 {
56     discardSnapshotImages();
57 }
58
59 ViewSnapshotStore& ViewSnapshotStore::singleton()
60 {
61     static ViewSnapshotStore& store = *new ViewSnapshotStore;
62     return store;
63 }
64
65 #if USE_RENDER_SERVER_VIEW_SNAPSHOTS
66 CAContext *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
83 void 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
90 void ViewSnapshotStore::willRemoveImageFromSnapshot(ViewSnapshot& snapshot)
91 {
92     bool removed = m_snapshotsWithImages.remove(&snapshot);
93     ASSERT_UNUSED(removed, removed);
94     m_snapshotCacheSize -= snapshot.imageSizeInBytes();
95 }
96
97 void 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
109 void ViewSnapshotStore::recordSnapshot(WebPageProxy& webPageProxy, WebBackForwardListItem& item)
110 {
111     if (webPageProxy.isShowingNavigationGestureSnapshot())
112         return;
113
114     pruneSnapshots(webPageProxy);
115
116     webPageProxy.willRecordNavigationSnapshot(item);
117
118     RefPtr<ViewSnapshot> snapshot = webPageProxy.takeViewSnapshot();
119     if (!snapshot || !snapshot->hasImage())
120         return;
121
122     snapshot->setRenderTreeSize(webPageProxy.renderTreeSize());
123     snapshot->setDeviceScaleFactor(webPageProxy.deviceScaleFactor());
124     snapshot->setBackgroundColor(webPageProxy.pageExtendedBackgroundColor());
125
126     item.setSnapshot(snapshot.release());
127 }
128
129 void ViewSnapshotStore::discardSnapshotImages()
130 {
131     while (!m_snapshotsWithImages.isEmpty())
132         m_snapshotsWithImages.first()->clearImage();
133 }
134
135
136 #if USE_IOSURFACE_VIEW_SNAPSHOTS
137 PassRefPtr<ViewSnapshot> ViewSnapshot::create(IOSurface* surface, IntSize size, size_t imageSizeInBytes)
138 {
139     return adoptRef(new ViewSnapshot(surface, size, imageSizeInBytes));
140 }
141 #elif USE_RENDER_SERVER_VIEW_SNAPSHOTS
142 PassRefPtr<ViewSnapshot> ViewSnapshot::create(uint32_t slotID, IntSize size, size_t imageSizeInBytes)
143 {
144     return adoptRef(new ViewSnapshot(slotID, size, imageSizeInBytes));
145 }
146 #endif
147
148 #if USE_IOSURFACE_VIEW_SNAPSHOTS
149 ViewSnapshot::ViewSnapshot(IOSurface* surface, IntSize size, size_t imageSizeInBytes)
150     : m_surface(surface)
151 #elif USE_RENDER_SERVER_VIEW_SNAPSHOTS
152 ViewSnapshot::ViewSnapshot(uint32_t slotID, IntSize size, size_t imageSizeInBytes)
153     : m_slotID(slotID)
154 #endif
155     , m_imageSizeInBytes(imageSizeInBytes)
156     , m_size(size)
157 {
158     if (hasImage())
159         ViewSnapshotStore::singleton().didAddImageToSnapshot(*this);
160 }
161
162 ViewSnapshot::~ViewSnapshot()
163 {
164     clearImage();
165 }
166
167 bool ViewSnapshot::hasImage() const
168 {
169 #if USE_IOSURFACE_VIEW_SNAPSHOTS
170     return m_surface;
171 #elif USE_RENDER_SERVER_VIEW_SNAPSHOTS
172     return m_slotID;
173 #endif
174 }
175
176 void ViewSnapshot::clearImage()
177 {
178     if (!hasImage())
179         return;
180
181     ViewSnapshotStore::singleton().willRemoveImageFromSnapshot(*this);
182
183 #if USE_IOSURFACE_VIEW_SNAPSHOTS
184     m_surface = nullptr;
185 #elif USE_RENDER_SERVER_VIEW_SNAPSHOTS
186     [ViewSnapshotStore::snapshottingContext() deleteSlot:m_slotID];
187     m_slotID = 0;
188 #endif
189     m_imageSizeInBytes = 0;
190 }
191
192 id ViewSnapshot::asLayerContents()
193 {
194 #if USE_IOSURFACE_VIEW_SNAPSHOTS
195     if (!m_surface)
196         return nullptr;
197
198     if (m_surface->setIsVolatile(false) != IOSurface::SurfaceState::Valid) {
199         clearImage();
200         return nullptr;
201     }
202
203     return (id)m_surface->surface();
204 #elif USE_RENDER_SERVER_VIEW_SNAPSHOTS
205     return [CAContext objectForSlot:m_slotID];
206 #endif
207 }
208
209 } // namespace WebKit