summaryrefslogtreecommitdiff
path: root/Source/WebCore/loader/ResourceLoadStatistics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/loader/ResourceLoadStatistics.cpp')
-rw-r--r--Source/WebCore/loader/ResourceLoadStatistics.cpp358
1 files changed, 358 insertions, 0 deletions
diff --git a/Source/WebCore/loader/ResourceLoadStatistics.cpp b/Source/WebCore/loader/ResourceLoadStatistics.cpp
new file mode 100644
index 000000000..5aa181469
--- /dev/null
+++ b/Source/WebCore/loader/ResourceLoadStatistics.cpp
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "ResourceLoadStatistics.h"
+
+#include "KeyedCoding.h"
+#include <wtf/text/StringBuilder.h>
+#include <wtf/text/StringHash.h>
+
+namespace WebCore {
+
+typedef WTF::HashMap<String, unsigned, StringHash, HashTraits<String>, HashTraits<unsigned>>::KeyValuePairType ResourceLoadStatisticsValue;
+
+static void encodeHashCountedSet(KeyedEncoder& encoder, const String& label, const HashCountedSet<String>& hashCountedSet)
+{
+ if (hashCountedSet.isEmpty())
+ return;
+
+ encoder.encodeObjects(label, hashCountedSet.begin(), hashCountedSet.end(), [](KeyedEncoder& encoderInner, const ResourceLoadStatisticsValue& origin) {
+ encoderInner.encodeString("origin", origin.key);
+ encoderInner.encodeUInt32("count", origin.value);
+ });
+}
+
+void ResourceLoadStatistics::encode(KeyedEncoder& encoder) const
+{
+ encoder.encodeString("PrevalentResourceOrigin", highLevelDomain);
+
+ // User interaction
+ encoder.encodeBool("hadUserInteraction", hadUserInteraction);
+ encoder.encodeDouble("mostRecentUserInteraction", mostRecentUserInteraction);
+ encoder.encodeBool("grandfathered", grandfathered);
+
+ // Top frame stats
+ encoder.encodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore);
+ encoder.encodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo);
+ encoder.encodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom);
+ encoder.encodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount);
+ encoder.encodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo);
+ encoder.encodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom);
+
+ // Subframe stats
+ encoder.encodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore);
+ encoder.encodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo);
+ encoder.encodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom);
+ encoder.encodeUInt32("subframeSubResourceCount", subframeSubResourceCount);
+ encodeHashCountedSet(encoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
+ encodeHashCountedSet(encoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
+ encoder.encodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo);
+ encoder.encodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom);
+
+ // Subresource stats
+ encoder.encodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom);
+ encoder.encodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo);
+ encoder.encodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount);
+ encoder.encodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited);
+ encodeHashCountedSet(encoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
+ encodeHashCountedSet(encoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
+
+ // Prevalent Resource
+ encodeHashCountedSet(encoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
+ encoder.encodeBool("isPrevalentResource", isPrevalentResource);
+ encoder.encodeUInt32("dataRecordsRemoved", dataRecordsRemoved);
+}
+
+static void decodeHashCountedSet(KeyedDecoder& decoder, const String& label, HashCountedSet<String>& hashCountedSet)
+{
+ Vector<String> ignore;
+ decoder.decodeObjects(label, ignore, [&hashCountedSet](KeyedDecoder& decoderInner, String& origin) {
+ if (!decoderInner.decodeString("origin", origin))
+ return false;
+
+ unsigned count;
+ if (!decoderInner.decodeUInt32("count", count))
+ return false;
+
+ hashCountedSet.add(origin, count);
+ return true;
+ });
+}
+
+bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, unsigned version)
+{
+ if (!decoder.decodeString("PrevalentResourceOrigin", highLevelDomain))
+ return false;
+
+ // User interaction
+ if (!decoder.decodeBool("hadUserInteraction", hadUserInteraction))
+ return false;
+
+ // Top frame stats
+ if (!decoder.decodeBool("topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore))
+ return false;
+
+ if (!decoder.decodeUInt32("topFrameHasBeenRedirectedTo", topFrameHasBeenRedirectedTo))
+ return false;
+
+ if (!decoder.decodeUInt32("topFrameHasBeenRedirectedFrom", topFrameHasBeenRedirectedFrom))
+ return false;
+
+ if (!decoder.decodeUInt32("topFrameInitialLoadCount", topFrameInitialLoadCount))
+ return false;
+
+ if (!decoder.decodeUInt32("topFrameHasBeenNavigatedTo", topFrameHasBeenNavigatedTo))
+ return false;
+
+ if (!decoder.decodeUInt32("topFrameHasBeenNavigatedFrom", topFrameHasBeenNavigatedFrom))
+ return false;
+
+ // Subframe stats
+ if (!decoder.decodeBool("subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore))
+ return false;
+
+ if (!decoder.decodeUInt32("subframeHasBeenRedirectedTo", subframeHasBeenRedirectedTo))
+ return false;
+
+ if (!decoder.decodeUInt32("subframeHasBeenRedirectedFrom", subframeHasBeenRedirectedFrom))
+ return false;
+
+ if (!decoder.decodeUInt32("subframeSubResourceCount", subframeSubResourceCount))
+ return false;
+
+ decodeHashCountedSet(decoder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
+ decodeHashCountedSet(decoder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
+
+ if (!decoder.decodeUInt32("subframeHasBeenNavigatedTo", subframeHasBeenNavigatedTo))
+ return false;
+
+ if (!decoder.decodeUInt32("subframeHasBeenNavigatedFrom", subframeHasBeenNavigatedFrom))
+ return false;
+
+ // Subresource stats
+ if (!decoder.decodeUInt32("subresourceHasBeenRedirectedFrom", subresourceHasBeenRedirectedFrom))
+ return false;
+
+ if (!decoder.decodeUInt32("subresourceHasBeenRedirectedTo", subresourceHasBeenRedirectedTo))
+ return false;
+
+ if (!decoder.decodeUInt32("subresourceHasBeenSubresourceCount", subresourceHasBeenSubresourceCount))
+ return false;
+
+ if (!decoder.decodeDouble("subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited", subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited))
+ return false;
+
+ decodeHashCountedSet(decoder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
+ decodeHashCountedSet(decoder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
+
+ // Prevalent Resource
+ decodeHashCountedSet(decoder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
+
+ if (!decoder.decodeBool("isPrevalentResource", isPrevalentResource))
+ return false;
+
+ if (version < 2)
+ return true;
+
+ if (!decoder.decodeUInt32("dataRecordsRemoved", dataRecordsRemoved))
+ return false;
+
+ if (version < 3)
+ return true;
+
+ if (!decoder.decodeDouble("mostRecentUserInteraction", mostRecentUserInteraction))
+ return false;
+
+ if (!decoder.decodeBool("grandfathered", grandfathered))
+ return false;
+
+ return true;
+}
+
+static void appendBoolean(StringBuilder& builder, const String& label, bool flag)
+{
+ builder.appendLiteral(" ");
+ builder.append(label);
+ builder.appendLiteral(": ");
+ builder.append(flag ? "Yes" : "No");
+}
+
+static void appendHashCountedSet(StringBuilder& builder, const String& label, const HashCountedSet<String>& hashCountedSet)
+{
+ if (hashCountedSet.isEmpty())
+ return;
+
+ builder.appendLiteral(" ");
+ builder.append(label);
+ builder.appendLiteral(":\n");
+
+ for (auto& entry : hashCountedSet) {
+ builder.appendLiteral(" ");
+ builder.append(entry.key);
+ builder.appendLiteral(": ");
+ builder.appendNumber(entry.value);
+ builder.append('\n');
+ }
+}
+
+String ResourceLoadStatistics::toString() const
+{
+ StringBuilder builder;
+
+ // User interaction
+ appendBoolean(builder, "hadUserInteraction", hadUserInteraction);
+ builder.append('\n');
+ builder.appendLiteral(" mostRecentUserInteraction: ");
+ builder.appendNumber(mostRecentUserInteraction);
+ builder.append('\n');
+ appendBoolean(builder, " grandfathered", grandfathered);
+ builder.append('\n');
+
+ // Top frame stats
+ appendBoolean(builder, "topFrameHasBeenNavigatedToBefore", topFrameHasBeenNavigatedToBefore);
+ builder.append('\n');
+ builder.appendLiteral(" topFrameHasBeenRedirectedTo: ");
+ builder.appendNumber(topFrameHasBeenRedirectedTo);
+ builder.append('\n');
+ builder.appendLiteral(" topFrameHasBeenRedirectedFrom: ");
+ builder.appendNumber(topFrameHasBeenRedirectedFrom);
+ builder.append('\n');
+ builder.appendLiteral(" topFrameInitialLoadCount: ");
+ builder.appendNumber(topFrameInitialLoadCount);
+ builder.append('\n');
+ builder.appendLiteral(" topFrameHasBeenNavigatedTo: ");
+ builder.appendNumber(topFrameHasBeenNavigatedTo);
+ builder.append('\n');
+ builder.appendLiteral(" topFrameHasBeenNavigatedFrom: ");
+ builder.appendNumber(topFrameHasBeenNavigatedFrom);
+ builder.append('\n');
+
+ // Subframe stats
+ appendBoolean(builder, "subframeHasBeenLoadedBefore", subframeHasBeenLoadedBefore);
+ builder.append('\n');
+ builder.appendLiteral(" subframeHasBeenRedirectedTo: ");
+ builder.appendNumber(subframeHasBeenRedirectedTo);
+ builder.append('\n');
+ builder.appendLiteral(" subframeHasBeenRedirectedFrom: ");
+ builder.appendNumber(subframeHasBeenRedirectedFrom);
+ builder.append('\n');
+ builder.appendLiteral(" subframeSubResourceCount: ");
+ builder.appendNumber(subframeSubResourceCount);
+ builder.append('\n');
+ appendHashCountedSet(builder, "subframeUnderTopFrameOrigins", subframeUnderTopFrameOrigins);
+ appendHashCountedSet(builder, "subframeUniqueRedirectsTo", subframeUniqueRedirectsTo);
+ builder.appendLiteral(" subframeHasBeenNavigatedTo: ");
+ builder.appendNumber(subframeHasBeenNavigatedTo);
+ builder.append('\n');
+ builder.appendLiteral(" subframeHasBeenNavigatedFrom: ");
+ builder.appendNumber(subframeHasBeenNavigatedFrom);
+ builder.append('\n');
+
+ // Subresource stats
+ builder.appendLiteral(" subresourceHasBeenRedirectedFrom: ");
+ builder.appendNumber(subresourceHasBeenRedirectedFrom);
+ builder.append('\n');
+ builder.appendLiteral(" subresourceHasBeenRedirectedTo: ");
+ builder.appendNumber(subresourceHasBeenRedirectedTo);
+ builder.append('\n');
+ builder.appendLiteral(" subresourceHasBeenSubresourceCount: ");
+ builder.appendNumber(subresourceHasBeenSubresourceCount);
+ builder.append('\n');
+ builder.appendLiteral(" subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited: ");
+ builder.appendNumber(subresourceHasBeenSubresourceCountDividedByTotalNumberOfOriginsVisited);
+ builder.append('\n');
+ appendHashCountedSet(builder, "subresourceUnderTopFrameOrigins", subresourceUnderTopFrameOrigins);
+ appendHashCountedSet(builder, "subresourceUniqueRedirectsTo", subresourceUniqueRedirectsTo);
+
+ // Prevalent Resource
+ appendHashCountedSet(builder, "redirectedToOtherPrevalentResourceOrigins", redirectedToOtherPrevalentResourceOrigins);
+ appendBoolean(builder, "isPrevalentResource", isPrevalentResource);
+ builder.appendLiteral(" dataRecordsRemoved: ");
+ builder.appendNumber(dataRecordsRemoved);
+ builder.append('\n');
+
+ builder.append('\n');
+
+ return builder.toString();
+}
+
+template <typename T>
+static void mergeHashCountedSet(HashCountedSet<T>& to, const HashCountedSet<T>& from)
+{
+ for (auto& entry : from)
+ to.add(entry.key, entry.value);
+}
+
+void ResourceLoadStatistics::merge(const ResourceLoadStatistics& other)
+{
+ ASSERT(other.highLevelDomain == highLevelDomain);
+
+ if (!other.hadUserInteraction) {
+ // If user interaction has been reset do so here too.
+ // Else, do nothing.
+ if (!other.mostRecentUserInteraction) {
+ hadUserInteraction = false;
+ mostRecentUserInteraction = 0;
+ }
+ } else {
+ hadUserInteraction = true;
+ if (mostRecentUserInteraction < other.mostRecentUserInteraction)
+ mostRecentUserInteraction = other.mostRecentUserInteraction;
+ }
+ grandfathered |= other.grandfathered;
+
+ // Top frame stats
+ topFrameHasBeenRedirectedTo += other.topFrameHasBeenRedirectedTo;
+ topFrameHasBeenRedirectedFrom += other.topFrameHasBeenRedirectedFrom;
+ topFrameInitialLoadCount += other.topFrameInitialLoadCount;
+ topFrameHasBeenNavigatedTo += other.topFrameHasBeenNavigatedTo;
+ topFrameHasBeenNavigatedFrom += other.topFrameHasBeenNavigatedFrom;
+ topFrameHasBeenNavigatedToBefore |= other.topFrameHasBeenNavigatedToBefore;
+
+ // Subframe stats
+ mergeHashCountedSet(subframeUnderTopFrameOrigins, other.subframeUnderTopFrameOrigins);
+ subframeHasBeenRedirectedTo += other.subframeHasBeenRedirectedTo;
+ subframeHasBeenRedirectedFrom += other.subframeHasBeenRedirectedFrom;
+ mergeHashCountedSet(subframeUniqueRedirectsTo, other.subframeUniqueRedirectsTo);
+ subframeSubResourceCount += other.subframeSubResourceCount;
+ subframeHasBeenNavigatedTo += other.subframeHasBeenNavigatedTo;
+ subframeHasBeenNavigatedFrom += other.subframeHasBeenNavigatedFrom;
+ subframeHasBeenLoadedBefore |= other.subframeHasBeenLoadedBefore;
+
+ // Subresource stats
+ mergeHashCountedSet(subresourceUnderTopFrameOrigins, other.subresourceUnderTopFrameOrigins);
+ subresourceHasBeenSubresourceCount += other.subresourceHasBeenSubresourceCount;
+ subresourceHasBeenRedirectedFrom += other.subresourceHasBeenRedirectedFrom;
+ subresourceHasBeenRedirectedTo += other.subresourceHasBeenRedirectedTo;
+ mergeHashCountedSet(subresourceUniqueRedirectsTo, other.subresourceUniqueRedirectsTo);
+
+ // Prevalent resource stats
+ mergeHashCountedSet(redirectedToOtherPrevalentResourceOrigins, other.redirectedToOtherPrevalentResourceOrigins);
+ isPrevalentResource |= other.isPrevalentResource;
+ dataRecordsRemoved += other.dataRecordsRemoved;
+}
+
+}