de13a5f47e46a0e64120c344e55ee81585d6b6c1
[WebKit-https.git] / Source / WebCore / loader / ResourceLoadStatistics.cpp
1 /*
2  * Copyright (C) 2016 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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ResourceLoadStatistics.h"
28
29 #include "KeyedCoding.h"
30 #include <wtf/text/StringBuilder.h>
31 #include <wtf/text/StringHash.h>
32
33 namespace WebCore {
34
35 static const unsigned minimumOriginsVisitedForPrevalenceClassification = 100;
36
37 // Sub frame thresholds
38 static const unsigned subframeUnderTopFrameOriginsThresholdAbsolute = 3;
39
40 // Subresource thresholds
41 static const unsigned subresourceUnderTopFrameOriginsThresholdAbsolute = 5;
42 static const unsigned subresourceHasBeenRedirectedFromToUniqueDomainsThresholdAbsolute = 3;
43 static const unsigned redirectedToOtherPrevalentResourceOriginsThresholdAbsolute = 2;
44
45 bool ResourceLoadStatistics::checkAndSetAsPrevalentResourceIfNecessary(unsigned originsVisitedSoFar)
46 {
47     if (originsVisitedSoFar < minimumOriginsVisitedForPrevalenceClassification || isPrevalentResource)
48         return false;
49
50     if (hasPrevalentResourceCharacteristics()) {
51         isPrevalentResource = true;
52         return true;
53     }
54
55     return false;
56 }
57
58 bool ResourceLoadStatistics::hasPrevalentResourceCharacteristics() const
59 {
60     return subframeUnderTopFrameOrigins.size() > subframeUnderTopFrameOriginsThresholdAbsolute
61         || subresourceUnderTopFrameOrigins.size() > subresourceUnderTopFrameOriginsThresholdAbsolute
62         || subresourceUniqueRedirectsTo.size() > subresourceHasBeenRedirectedFromToUniqueDomainsThresholdAbsolute
63         || redirectedToOtherPrevalentResourceOrigins.size() > redirectedToOtherPrevalentResourceOriginsThresholdAbsolute;
64 }
65
66 typedef WTF::HashMap<String, unsigned, StringHash, HashTraits<String>, HashTraits<unsigned>>::KeyValuePairType ResourceLoadStatisticsValue;
67
68 static void encodeHashCountedSet(KeyedEncoder& encoder, const String& label, const HashCountedSet<String>& hashCountedSet)
69 {
70     if (hashCountedSet.isEmpty())
71         return;
72
73     encoder.encodeObjects(label, hashCountedSet.begin(), hashCountedSet.end(), [](KeyedEncoder& encoderInner, const ResourceLoadStatisticsValue& origin) {
74         encoderInner.encodeString("origin", origin.key);
75         encoderInner.encodeUInt32("count", origin.value);
76     });
77 }
78
79 void ResourceLoadStatistics::encode(KeyedEncoder& encoder, const String& origin) const
80 {
81     encoder.encodeString("PrevalentResourceOrigin", origin);
82     
83     // User interaction
84     encoder.encodeBool("hadUserInteraction", hadUserInteraction);
85     
86     // Top frame stats
87     encoder.encodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore);
88     encoder.encodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo);
89     encoder.encodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom);
90     encoder.encodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount);
91     encoder.encodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo);
92     encoder.encodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom);
93     
94     // Subframe stats
95     encoder.encodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore);
96     encoder.encodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo);
97     encoder.encodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom);
98     encoder.encodeUInt32("subframeSubResourceCount", subframeSubResourceCount);
99     encodeHashCountedSet(encoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
100     encodeHashCountedSet(encoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
101     encoder.encodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo);
102     encoder.encodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom);
103     
104     // Subresource stats
105     encoder.encodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom);
106     encoder.encodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo);
107     encoder.encodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount);
108     encoder.encodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited);
109     encodeHashCountedSet(encoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
110     encodeHashCountedSet(encoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
111     
112     // Prevalent Resource
113     encodeHashCountedSet(encoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
114     encoder.encodeBool("isPrevalentResource", isPrevalentResource);
115 }
116
117 static void decodeHashCountedSet(KeyedDecoder& decoder, const String& label, HashCountedSet<String>& hashCountedSet)
118 {
119     Vector<String> ignore;
120     decoder.decodeObjects(label, ignore, [&hashCountedSet](KeyedDecoder& decoderInner, String& origin) {
121         if (!decoderInner.decodeString("origin", origin))
122             return false;
123         
124         unsigned count;
125         if (!decoderInner.decodeUInt32("count", count))
126             return false;
127
128         hashCountedSet.add(origin, count);
129         return true;
130     });
131 }
132
133 bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, const String& origin)
134 {
135     String storedOrigin;
136     if (!decoder.decodeString("PrevalentResourceOrigin", storedOrigin))
137         return false;
138     
139     ASSERT_UNUSED(origin, storedOrigin == origin);
140     
141     // User interaction
142     if (!decoder.decodeBool("hadUserInteraction", hadUserInteraction))
143         return false;
144     
145     // Top frame stats
146     if (!decoder.decodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore))
147         return false;
148     
149     if (!decoder.decodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo))
150         return false;
151     
152     if (!decoder.decodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom))
153         return false;
154     
155     if (!decoder.decodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount))
156         return false;
157     
158     if (!decoder.decodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo))
159         return false;
160     
161     if (!decoder.decodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom))
162         return false;
163     
164     // Subframe stats
165     if (!decoder.decodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore))
166         return false;
167     
168     if (!decoder.decodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo))
169         return false;
170     
171     if (!decoder.decodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom))
172         return false;
173     
174     if (!decoder.decodeUInt32("subframeSubResourceCount", subframeSubResourceCount))
175         return false;
176
177     decodeHashCountedSet(decoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
178     decodeHashCountedSet(decoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
179     
180     if (!decoder.decodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo))
181         return false;
182     
183     if (!decoder.decodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom))
184         return false;
185     
186     // Subresource stats
187     if (!decoder.decodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom))
188         return false;
189     
190     if (!decoder.decodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo))
191         return false;
192     
193     if (!decoder.decodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount))
194         return false;
195     
196     if (!decoder.decodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited))
197         return false;
198
199     decodeHashCountedSet(decoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
200     decodeHashCountedSet(decoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
201     
202     // Prevalent Resource
203     decodeHashCountedSet(decoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
204     
205     if (!decoder.decodeBool("isPrevalentResource", isPrevalentResource))
206         return false;
207     
208     return true;
209 }
210
211 static void appendBoolean(StringBuilder& builder, const String& label, bool flag)
212 {
213     builder.append("    ");
214     builder.append(label);
215     builder.append(": ");
216     builder.append(flag ? "Yes" : "No");
217 }
218
219 static void appendHashCountedSet(StringBuilder& builder, const String& label, const HashCountedSet<String>& hashCountedSet)
220 {
221     if (hashCountedSet.isEmpty())
222         return;
223
224     builder.append("    ");
225     builder.append(label);
226     builder.append(":\n");
227
228     for (auto& entry : hashCountedSet) {
229         builder.append("        ");
230         builder.append(entry.key);
231         builder.append(": ");
232         builder.appendNumber(entry.value);
233         builder.append('\n');
234     }
235     
236 }
237
238 String ResourceLoadStatistics::toString() const
239 {
240     StringBuilder builder;
241     
242     // User interaction
243     appendBoolean(builder, "hadUserInteraction", hadUserInteraction);
244     builder.append('\n');
245     
246     // Top frame stats
247     appendBoolean(builder, "topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore);
248     builder.append('\n');
249     builder.append("    topFrameHasBeenRedirectedTo: ");
250     builder.appendNumber(topFrameHasBeenRedirectedTo);
251     builder.append('\n');
252     builder.append("    topFrameHasBeenRedirectedFrom: ");
253     builder.appendNumber(topFrameHasBeenRedirectedFrom);
254     builder.append('\n');
255     builder.append("    topFrameInitialLoadCount: ");
256     builder.appendNumber(topFrameInitialLoadCount);
257     builder.append('\n');
258     builder.append("    topFrameHasBeenNavigatedTo: ");
259     builder.appendNumber(topFrameHasBeenNavigatedTo);
260     builder.append('\n');
261     builder.append("    topFrameHasBeenNavigatedFrom: ");
262     builder.appendNumber(topFrameHasBeenNavigatedFrom);
263     builder.append('\n');
264     
265     // Subframe stats
266     appendBoolean(builder, "subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore);
267     builder.append('\n');
268     builder.append("    subframeHasBeenRedirectedTo: ");
269     builder.appendNumber(subframeHasBeenRedirectedTo);
270     builder.append('\n');
271     builder.append("    subframeHasBeenRedirectedFrom: ");
272     builder.appendNumber(subframeHasBeenRedirectedFrom);
273     builder.append('\n');
274     builder.append("    subframeSubResourceCount: ");
275     builder.appendNumber(subframeSubResourceCount);
276     builder.append('\n');
277     appendHashCountedSet(builder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
278     appendHashCountedSet(builder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
279     builder.append("    subframeHasBeenNavigatedTo: ");
280     builder.appendNumber(subframeHasBeenNavigatedTo);
281     builder.append('\n');
282     builder.append("    subframeHasBeenNavigatedFrom: ");
283     builder.appendNumber(subframeHasBeenNavigatedFrom);
284     builder.append('\n');
285     
286     // Subresource stats
287     builder.append("    subresourceHasBeenRedirectedFrom: ");
288     builder.appendNumber(subresourceHasBeenRedirectedFrom);
289     builder.append('\n');
290     builder.append("    subresourceHasBeenRedirectedTo: ");
291     builder.appendNumber(subresourceHasBeenRedirectedTo);
292     builder.append('\n');
293     builder.append("    subresourceHasBeenSubresourceCount: ");
294     builder.appendNumber(subresourceHasBeenSubresourceCount);
295     builder.append('\n');
296     builder.append("    subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited: ");
297     builder.appendNumber(subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited);
298     builder.append('\n');
299     appendHashCountedSet(builder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
300     appendHashCountedSet(builder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
301     
302     // Prevalent Resource
303     appendHashCountedSet(builder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
304     appendBoolean(builder, "isPrevalentResource", isPrevalentResource);
305     builder.append('\n');
306
307     return builder.toString();
308 }
309
310 }