Add Web API Statistics Collection
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Sep 2018 17:14:07 +0000 (17:14 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Sep 2018 17:14:07 +0000 (17:14 +0000)
https://bugs.webkit.org/show_bug.cgi?id=187773
<rdar://problem/44155162>

Patch by Woodrow Wang <woodrow_wang@apple.com> on 2018-09-11
Reviewed by Brent Fulgham.

Source/WebCore:

Added data collection for web API statistics, specifically regarding the canvas, font loads,
screen functions, and navigator functions. The data collection code is placed under a runtime
enabled feature flag. The statistics are stored in a ResourceLoadStatistics object and written
to a plist on disk. Added a new file CanvasActivityRecord.h and CanvasActivityRecord.cpp which
includes a struct to keep track of HTML5 canvas element read and writes.

Tests: http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html
       http/tests/webAPIStatistics/font-load-data-collection.html
       http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html
       http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html

* Sources.txt:
* WebCore.xcodeproj/project.pbxproj:
* css/CSSFontFaceSource.cpp:
(WebCore::CSSFontFaceSource::load):
* css/CSSFontSelector.cpp:
(WebCore::CSSFontSelector::fontRangesForFamily):
(WebCore::CSSFontSelector::fallbackFontAt):

The following are the functions where we'd like to record a canvas read.

* html/HTMLCanvasElement.cpp:
(WebCore::HTMLCanvasElement::toDataURL):
(WebCore::HTMLCanvasElement::toBlob):
(WebCore::HTMLCanvasElement::getImageData):
(WebCore::HTMLCanvasElement::toMediaSample):
(WebCore::HTMLCanvasElement::captureStream):

The following are the functions where we'd like to record a canvas write.

* html/canvas/CanvasRenderingContext2D.cpp:
(WebCore::CanvasRenderingContext2D::measureText):
(WebCore::CanvasRenderingContext2D::drawTextInternal):

The following files and functions handle the CanvasActivityRecord struct and
its respective functions.

* loader/CanvasActivityRecord.cpp: Added.
(WebCore::CanvasActivityRecord::recordWrittenOrMeasuredText):
(WebCore::CanvasActivityRecord::mergeWith):
* loader/CanvasActivityRecord.h: Added.
(WebCore::CanvasActivityRecord::encode const):
(WebCore::CanvasActivityRecord::decode):

* loader/DocumentThreadableLoader.cpp:
* loader/FrameLoader.cpp:
* loader/ResourceLoadObserver.cpp:
(WebCore::ResourceLoadObserver::logFontLoad):
(WebCore::ResourceLoadObserver::logCanvasRead):
(WebCore::ResourceLoadObserver::logCanvasWriteOrMeasure):
(WebCore::ResourceLoadObserver::logNavigatorAPIAccessed):
(WebCore::ResourceLoadObserver::logScreenAPIAccessed):

Before, entries in the ResourceLoadStatistics involving HashSets used "origin" as the key.
Now the encodeHashSet function has been generalized to take any key to encode the entries
in the HashSet. Also added functionality to encode an OptionSet by converting it to its
raw bitmask state.

* loader/ResourceLoadObserver.h:
* loader/ResourceLoadStatistics.cpp:
(WebCore::encodeHashSet):
(WebCore::encodeOriginHashSet):
(WebCore::encodeOptionSet):
(WebCore::encodeFontHashSet):
(WebCore::encodeCanvasActivityRecord):
(WebCore::ResourceLoadStatistics::encode const):
(WebCore::decodeHashSet):
(WebCore::decodeOriginHashSet):
(WebCore::decodeOptionSet):
(WebCore::decodeFontHashSet):
(WebCore::decodeCanvasActivityRecord):
(WebCore::ResourceLoadStatistics::decode):
(WebCore::navigatorAPIEnumToString):
(WebCore::screenAPIEnumToString):
(WebCore::appendNavigatorAPIOptionSet):
(WebCore::appendScreenAPIOptionSet):
(WebCore::ResourceLoadStatistics::toString const):
(WebCore::ResourceLoadStatistics::merge):
* loader/ResourceLoadStatistics.h:
* loader/ResourceTiming.cpp:

The following are the navigator functions recorded for the web API statistics.

* page/Navigator.cpp:
(WebCore::Navigator::appVersion const):
(WebCore::Navigator::userAgent const):
(WebCore::Navigator::plugins):
(WebCore::Navigator::mimeTypes):
(WebCore::Navigator::cookieEnabled const):
(WebCore::Navigator::javaEnabled const):

The following are the screen functions recorded for the web API statistics.

* page/Screen.cpp:
(WebCore::Screen::height const):
(WebCore::Screen::width const):
(WebCore::Screen::colorDepth const):
(WebCore::Screen::pixelDepth const):
(WebCore::Screen::availLeft const):
(WebCore::Screen::availTop const):
(WebCore::Screen::availHeight const):
(WebCore::Screen::availWidth const):

Source/WebKit:

* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<ResourceLoadStatistics>::encode):
(IPC::ArgumentCoder<ResourceLoadStatistics>::decode):
* UIProcess/ResourceLoadStatisticsMemoryStore.cpp:

LayoutTests:

Added new tests and expectations for the web API statistics data collection.

* TestExpectations:
* http/tests/webAPIStatistics/canvas-read-and-write-data-collection-expected.txt: Added.
* http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html: Added.
* http/tests/webAPIStatistics/font-load-data-collection-expected.txt: Added.
* http/tests/webAPIStatistics/font-load-data-collection.html: Added.
* http/tests/webAPIStatistics/navigator-functions-accessed-data-collection-expected.txt: Added.
* http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html: Added.
* http/tests/webAPIStatistics/screen-functions-accessed-data-collection-expected.txt: Added.
* http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html: Added.
* platform/ios-wk2/TestExpectations:
* platform/mac-wk2/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@235897 268f45cc-cd09-0410-ab3c-d52691b4dbfc

33 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/font-load-data-collection-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/font-load-data-collection.html [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html [new file with mode: 0644]
LayoutTests/platform/ios-wk2/TestExpectations
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/Sources.txt
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/css/CSSFontFaceSource.cpp
Source/WebCore/css/CSSFontSelector.cpp
Source/WebCore/html/HTMLCanvasElement.cpp
Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp
Source/WebCore/loader/CanvasActivityRecord.cpp [new file with mode: 0644]
Source/WebCore/loader/CanvasActivityRecord.h [new file with mode: 0644]
Source/WebCore/loader/DocumentThreadableLoader.cpp
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/ResourceLoadObserver.cpp
Source/WebCore/loader/ResourceLoadObserver.h
Source/WebCore/loader/ResourceLoadStatistics.cpp
Source/WebCore/loader/ResourceLoadStatistics.h
Source/WebCore/loader/ResourceTiming.cpp
Source/WebCore/page/Navigator.cpp
Source/WebCore/page/Screen.cpp
Source/WebKit/ChangeLog
Source/WebKit/Shared/WebCoreArgumentCoders.cpp
Source/WebKit/UIProcess/ResourceLoadStatisticsMemoryStore.cpp

index d252412..77060e2 100644 (file)
@@ -1,3 +1,25 @@
+2018-09-11  Woodrow Wang  <woodrow_wang@apple.com>
+
+        Add Web API Statistics Collection
+        https://bugs.webkit.org/show_bug.cgi?id=187773
+        <rdar://problem/44155162>
+
+        Reviewed by Brent Fulgham.
+
+        Added new tests and expectations for the web API statistics data collection.
+
+        * TestExpectations:
+        * http/tests/webAPIStatistics/canvas-read-and-write-data-collection-expected.txt: Added.
+        * http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html: Added.
+        * http/tests/webAPIStatistics/font-load-data-collection-expected.txt: Added.
+        * http/tests/webAPIStatistics/font-load-data-collection.html: Added.
+        * http/tests/webAPIStatistics/navigator-functions-accessed-data-collection-expected.txt: Added.
+        * http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html: Added.
+        * http/tests/webAPIStatistics/screen-functions-accessed-data-collection-expected.txt: Added.
+        * http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html: Added.
+        * platform/ios-wk2/TestExpectations:
+        * platform/mac-wk2/TestExpectations:
+
 2018-09-11  Frederic Wang  <fwang@igalia.com>
 
         Modify more tests to use document.scrollingElement to access viewport scroll properties
index 4fc5d62..ea0d91b 100644 (file)
@@ -2245,3 +2245,5 @@ fast/gradients/conic-off-center.html [ Skip ]
 fast/gradients/conic-center-outside-box.html [ Skip ]
 fast/gradients/conic-extended-stops.html [ Skip ]
 fast/gradients/conic-from-angle.html [ Skip ]
+
+webkit.org/b/187773 http/tests/webAPIStatistics [ Skip ]
diff --git a/LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection-expected.txt b/LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection-expected.txt
new file mode 100644 (file)
index 0000000..16a6e69
--- /dev/null
@@ -0,0 +1,30 @@
+Tests for canvas read and write data collection in ResourceLoadStatistics plist by rendering and reading text on the canvas and dumping the entire resource load statistics map.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+Resource load statistics:
+
+High level domain: 127.0.0.1
+    lastSeen: 0
+    hadUserInteraction: No
+    mostRecentUserInteraction: -1
+    grandfathered: No
+    isPrevalentResource: No
+    isVeryPrevalentResource: No
+    dataRecordsRemoved: 0
+    isMarkedForCookieBlocking: No
+    fontsSuccessfullyLoaded:
+        Helvetica
+        Times
+        Courier
+    topFrameRegistrableDomainsWhichAccessedWebAPIs:
+        127.0.0.1: 8
+    canvasTextWritten:
+        suspicious invisible text
+    canvasReadData: Yes
+
diff --git a/LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html b/LayoutTests/http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html
new file mode 100644 (file)
index 0000000..153b09b
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<head>
+    <meta charset="UTF-8">
+    <title>Test for canvas read and write data collection in resource load statistics</title>
+    <script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+    description("Tests for canvas read and write data collection in ResourceLoadStatistics plist by rendering and reading text on the canvas and dumping the entire resource load statistics map.");
+    const hostUnderTest = "127.0.0.1:8000";
+    const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
+    function completeTest() {
+        testRunner.dumpResourceLoadStatistics();
+            
+        testRunner.statisticsResetToConsistentState(function() {
+            testRunner.notifyDone();
+        });
+    }
+
+    function runTestRunnerTest() {
+        testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(true);
+
+        testRunner.installStatisticsDidScanDataRecordsCallback(completeTest);
+    
+        var canvas = document.createElement('canvas');
+        var context = canvas.getContext('2d');
+        context.fillText('suspicious invisible text', 2, 15);
+        canvas.toDataURL();
+    }
+
+    if (document.location.host === hostUnderTest && window.testRunner && window.internals) {
+        testRunner.waitUntilDone();
+        internals.setResourceLoadStatisticsEnabled(true);
+        testRunner.setWebAPIStatisticsEnabled(true);
+        runTestRunnerTest();
+    }
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/webAPIStatistics/font-load-data-collection-expected.txt b/LayoutTests/http/tests/webAPIStatistics/font-load-data-collection-expected.txt
new file mode 100644 (file)
index 0000000..a9ef32f
--- /dev/null
@@ -0,0 +1,31 @@
+Tests for font loading data collection in ResourceLoadStatistics plist by loading fonts and dumping the entire resource load statistics map. The test tries to load various fonts through a comma separated font-family list to draw a string with many m's since they differ in width more prominently among fonts.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+mmmmmmmmmmlli
+Resource load statistics:
+
+High level domain: 127.0.0.1
+    lastSeen: 0
+    hadUserInteraction: No
+    mostRecentUserInteraction: -1
+    grandfathered: No
+    isPrevalentResource: No
+    isVeryPrevalentResource: No
+    dataRecordsRemoved: 0
+    isMarkedForCookieBlocking: No
+    fontsFailedToLoad:
+        Fransiscan
+        Andale
+        notARealFont
+    fontsSuccessfullyLoaded:
+        Times
+        Courier
+    topFrameRegistrableDomainsWhichAccessedWebAPIs:
+        127.0.0.1: 9
+    canvasReadData: No
+
diff --git a/LayoutTests/http/tests/webAPIStatistics/font-load-data-collection.html b/LayoutTests/http/tests/webAPIStatistics/font-load-data-collection.html
new file mode 100644 (file)
index 0000000..bf92b3a
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<head>
+    <meta charset="UTF-8">
+    <title>Test for font loading data collection in resource load statistics</title>
+    <script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+    description("Tests for font loading data collection in ResourceLoadStatistics plist by loading fonts and dumping the entire resource load statistics map. The test tries to load various fonts through a comma separated font-family list to draw a string with many m's since they differ in width more prominently among fonts.");
+    const hostUnderTest = "127.0.0.1:8000";
+    const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
+    function completeTest() {
+        testRunner.dumpResourceLoadStatistics();
+            
+        testRunner.statisticsResetToConsistentState(function() {
+            testRunner.notifyDone();
+        });
+    }
+
+    function runTestRunnerTest() {
+        testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(true);
+
+        testRunner.installStatisticsDidScanDataRecordsCallback(completeTest);
+
+        var body = document.getElementsByTagName('body')[0]
+    
+        var span = document.createElement('span');
+        var testFontString = 'mmmmmmmmmmlli';
+        
+        span.innerHTML = testFontString;
+        span.style.fontFamily = 'Andale, Fransiscan, notARealFont, serif';
+        body.appendChild(span);
+    }
+
+    if (document.location.host === hostUnderTest && window.testRunner && window.internals) {
+        testRunner.waitUntilDone();
+        internals.setResourceLoadStatisticsEnabled(true);
+        testRunner.setWebAPIStatisticsEnabled(true);
+        runTestRunnerTest();
+    }
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection-expected.txt b/LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection-expected.txt
new file mode 100644 (file)
index 0000000..0531068
--- /dev/null
@@ -0,0 +1,34 @@
+Tests for navigator functions accessed data collection in ResourceLoadStatistics plist by querying for all the navigator properties and dumping the entire resource load statistics map.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+Resource load statistics:
+
+High level domain: 127.0.0.1
+    lastSeen: 0
+    hadUserInteraction: No
+    mostRecentUserInteraction: -1
+    grandfathered: No
+    isPrevalentResource: No
+    isVeryPrevalentResource: No
+    dataRecordsRemoved: 0
+    isMarkedForCookieBlocking: No
+    fontsSuccessfullyLoaded:
+        Times
+        Courier
+    topFrameRegistrableDomainsWhichAccessedWebAPIs:
+        127.0.0.1: 12
+    navigatorFunctionsAccessed:
+        appVersion
+        userAgent
+        plugins
+        mimeTypes
+        cookieEnabled
+        javaEnabled
+    canvasReadData: No
+
diff --git a/LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html b/LayoutTests/http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html
new file mode 100644 (file)
index 0000000..bdbf164
--- /dev/null
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<head>
+    <meta charset="UTF-8">
+    <title>Test for navigator functions accessed data collection in resource load statistics</title>
+    <script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+    description("Tests for navigator functions accessed data collection in ResourceLoadStatistics plist by querying for all the navigator properties and dumping the entire resource load statistics map.");
+    const hostUnderTest = "127.0.0.1:8000";
+    const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
+    function completeTest() {
+        testRunner.dumpResourceLoadStatistics();
+            
+        testRunner.statisticsResetToConsistentState(function() {
+            testRunner.notifyDone();
+        });
+    }
+
+    function runTestRunnerTest() {
+        testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(true);
+
+        testRunner.installStatisticsDidScanDataRecordsCallback(completeTest);
+    
+        var useragent = navigator.userAgent;
+        var javaenabled = navigator.javaEnabled();
+        var cookieEnabled = navigator.cookieEnabled;
+        var mimetypes = navigator.mimeTypes;
+        var plugins = navigator.plugins;
+        var appversion = navigator.appVersion;
+    }
+
+    if (document.location.host === hostUnderTest && window.testRunner && window.internals) {
+        testRunner.waitUntilDone();
+        internals.setResourceLoadStatisticsEnabled(true);
+        testRunner.setWebAPIStatisticsEnabled(true);
+        runTestRunnerTest();
+    }
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection-expected.txt b/LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection-expected.txt
new file mode 100644 (file)
index 0000000..c1eaaa5
--- /dev/null
@@ -0,0 +1,36 @@
+Tests for screen functions accessed data collection in ResourceLoadStatistics plist by querying for all the screen properties and dumping the entire resource load statistics map.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+Resource load statistics:
+
+High level domain: 127.0.0.1
+    lastSeen: 0
+    hadUserInteraction: No
+    mostRecentUserInteraction: -1
+    grandfathered: No
+    isPrevalentResource: No
+    isVeryPrevalentResource: No
+    dataRecordsRemoved: 0
+    isMarkedForCookieBlocking: No
+    fontsSuccessfullyLoaded:
+        Times
+        Courier
+    topFrameRegistrableDomainsWhichAccessedWebAPIs:
+        127.0.0.1: 13
+    screenFunctionsAccessed:
+        height
+        width
+        colorDepth
+        pixelDepth
+        availLeft
+        availTop
+        availHeight
+        availWidth
+    canvasReadData: No
+
diff --git a/LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html b/LayoutTests/http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html
new file mode 100644 (file)
index 0000000..070310b
--- /dev/null
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<head>
+    <meta charset="UTF-8">
+    <title>Test for screen functions accessed data collection in resource load statistics</title>
+    <script src="/js-test-resources/js-test.js"></script>
+</head>
+<body>
+<script>
+    description("Tests for screen functions accessed data collection in ResourceLoadStatistics plist by querying for all the screen properties and dumping the entire resource load statistics map.");
+    const hostUnderTest = "127.0.0.1:8000";
+    const statisticsUrl = "http://" + hostUnderTest + "/temp";
+
+    function completeTest() {
+        testRunner.dumpResourceLoadStatistics();
+            
+        testRunner.statisticsResetToConsistentState(function() {
+            testRunner.notifyDone();
+        });
+    }
+
+    function runTestRunnerTest() {
+        testRunner.setStatisticsNotifyPagesWhenDataRecordsWereScanned(true);
+
+        testRunner.installStatisticsDidScanDataRecordsCallback(completeTest);
+    
+        var availTop = screen.availTop;
+        var colorDepth = screen.colorDepth;
+        var pixelDepth = screen.pixelDepth;
+        var height = screen.height;
+        var width = screen.width;
+        var availLeft = screen.availLeft;
+        var availHeight = screen.availHeight;
+        var availWidth = screen.availWidth;
+    }
+
+    if (document.location.host === hostUnderTest && window.testRunner && window.internals) {
+        testRunner.waitUntilDone();
+        internals.setResourceLoadStatisticsEnabled(true);
+        testRunner.setWebAPIStatisticsEnabled(true);
+        runTestRunnerTest();
+    }
+</script>
+</body>
+</html>
\ No newline at end of file
index 1ed6743..0b27c86 100644 (file)
@@ -50,6 +50,8 @@ http/tests/security/contentSecurityPolicy/manifest-src-allowed.html [ Pass ]
 http/tests/security/contentSecurityPolicy/manifest-src-blocked.html [ Pass ]
 applicationmanifest/ [ Pass ]
 
+webkit.org/b/187773 http/tests/webAPIStatistics [ Pass ]
+
 #//////////////////////////////////////////////////////////////////////////////////////////
 # End platform-specific directories.
 #//////////////////////////////////////////////////////////////////////////////////////////
index 53e4c9d..a3c501d 100644 (file)
@@ -57,6 +57,8 @@ webkit.org/b/187183 http/tests/security/pasteboard-file-url.html [ Pass ]
 
 fast/misc/valid-primary-screen-displayID.html [ Pass ]
 
+webkit.org/b/187773 http/tests/webAPIStatistics [ Pass ]
+
 #//////////////////////////////////////////////////////////////////////////////////////////
 # End platform-specific directories.
 #//////////////////////////////////////////////////////////////////////////////////////////
index 5e31f28..c7f206b 100644 (file)
@@ -1,3 +1,114 @@
+2018-09-11  Woodrow Wang  <woodrow_wang@apple.com>
+
+        Add Web API Statistics Collection
+        https://bugs.webkit.org/show_bug.cgi?id=187773
+        <rdar://problem/44155162>
+
+        Reviewed by Brent Fulgham.
+
+        Added data collection for web API statistics, specifically regarding the canvas, font loads, 
+        screen functions, and navigator functions. The data collection code is placed under a runtime 
+        enabled feature flag. The statistics are stored in a ResourceLoadStatistics object and written 
+        to a plist on disk. Added a new file CanvasActivityRecord.h and CanvasActivityRecord.cpp which
+        includes a struct to keep track of HTML5 canvas element read and writes. 
+
+        Tests: http/tests/webAPIStatistics/canvas-read-and-write-data-collection.html
+               http/tests/webAPIStatistics/font-load-data-collection.html
+               http/tests/webAPIStatistics/navigator-functions-accessed-data-collection.html
+               http/tests/webAPIStatistics/screen-functions-accessed-data-collection.html
+
+        * Sources.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * css/CSSFontFaceSource.cpp:
+        (WebCore::CSSFontFaceSource::load):
+        * css/CSSFontSelector.cpp:
+        (WebCore::CSSFontSelector::fontRangesForFamily):
+        (WebCore::CSSFontSelector::fallbackFontAt):
+
+        The following are the functions where we'd like to record a canvas read.
+
+        * html/HTMLCanvasElement.cpp:
+        (WebCore::HTMLCanvasElement::toDataURL):
+        (WebCore::HTMLCanvasElement::toBlob):
+        (WebCore::HTMLCanvasElement::getImageData):
+        (WebCore::HTMLCanvasElement::toMediaSample):
+        (WebCore::HTMLCanvasElement::captureStream):
+
+        The following are the functions where we'd like to record a canvas write.
+
+        * html/canvas/CanvasRenderingContext2D.cpp:
+        (WebCore::CanvasRenderingContext2D::measureText):
+        (WebCore::CanvasRenderingContext2D::drawTextInternal):
+
+        The following files and functions handle the CanvasActivityRecord struct and
+        its respective functions.
+
+        * loader/CanvasActivityRecord.cpp: Added.
+        (WebCore::CanvasActivityRecord::recordWrittenOrMeasuredText):
+        (WebCore::CanvasActivityRecord::mergeWith):
+        * loader/CanvasActivityRecord.h: Added.
+        (WebCore::CanvasActivityRecord::encode const):
+        (WebCore::CanvasActivityRecord::decode):
+
+        * loader/DocumentThreadableLoader.cpp:
+        * loader/FrameLoader.cpp:
+        * loader/ResourceLoadObserver.cpp:
+        (WebCore::ResourceLoadObserver::logFontLoad):
+        (WebCore::ResourceLoadObserver::logCanvasRead):
+        (WebCore::ResourceLoadObserver::logCanvasWriteOrMeasure):
+        (WebCore::ResourceLoadObserver::logNavigatorAPIAccessed):
+        (WebCore::ResourceLoadObserver::logScreenAPIAccessed):
+
+        Before, entries in the ResourceLoadStatistics involving HashSets used "origin" as the key. 
+        Now the encodeHashSet function has been generalized to take any key to encode the entries 
+        in the HashSet. Also added functionality to encode an OptionSet by converting it to its 
+        raw bitmask state. 
+
+        * loader/ResourceLoadObserver.h:
+        * loader/ResourceLoadStatistics.cpp:
+        (WebCore::encodeHashSet):
+        (WebCore::encodeOriginHashSet):
+        (WebCore::encodeOptionSet):
+        (WebCore::encodeFontHashSet):
+        (WebCore::encodeCanvasActivityRecord):
+        (WebCore::ResourceLoadStatistics::encode const):
+        (WebCore::decodeHashSet):
+        (WebCore::decodeOriginHashSet):
+        (WebCore::decodeOptionSet):
+        (WebCore::decodeFontHashSet):
+        (WebCore::decodeCanvasActivityRecord):
+        (WebCore::ResourceLoadStatistics::decode):
+        (WebCore::navigatorAPIEnumToString):
+        (WebCore::screenAPIEnumToString):
+        (WebCore::appendNavigatorAPIOptionSet):
+        (WebCore::appendScreenAPIOptionSet):
+        (WebCore::ResourceLoadStatistics::toString const):
+        (WebCore::ResourceLoadStatistics::merge):
+        * loader/ResourceLoadStatistics.h:
+        * loader/ResourceTiming.cpp:
+
+        The following are the navigator functions recorded for the web API statistics.
+
+        * page/Navigator.cpp:
+        (WebCore::Navigator::appVersion const):
+        (WebCore::Navigator::userAgent const):
+        (WebCore::Navigator::plugins):
+        (WebCore::Navigator::mimeTypes):
+        (WebCore::Navigator::cookieEnabled const):
+        (WebCore::Navigator::javaEnabled const):
+
+        The following are the screen functions recorded for the web API statistics.
+
+        * page/Screen.cpp:
+        (WebCore::Screen::height const):
+        (WebCore::Screen::width const):
+        (WebCore::Screen::colorDepth const):
+        (WebCore::Screen::pixelDepth const):
+        (WebCore::Screen::availLeft const):
+        (WebCore::Screen::availTop const):
+        (WebCore::Screen::availHeight const):
+        (WebCore::Screen::availWidth const):
+
 2018-09-11  Pablo Saavedra  <psaavedra@igalia.com>
 
         playbackControlsManagerUpdateTimerFired and
index e708db4..7e6af49 100644 (file)
@@ -1247,6 +1247,7 @@ layout/layouttree/LayoutInlineContainer.cpp
 layout/layouttree/LayoutReplaced.cpp
 layout/layouttree/LayoutTreeBuilder.cpp
 
+loader/CanvasActivityRecord.cpp
 loader/ContentFilter.cpp
 loader/CookieJar.cpp
 loader/CrossOriginAccessControl.cpp
index 1e63243..7293c6c 100644 (file)
                ED2BA83C09A24B91006C0AC4 /* DocumentMarker.h in Headers */ = {isa = PBXBuildFile; fileRef = ED2BA83B09A24B91006C0AC4 /* DocumentMarker.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EDE3A5000C7A430600956A37 /* ColorMac.h in Headers */ = {isa = PBXBuildFile; fileRef = EDE3A4FF0C7A430600956A37 /* ColorMac.h */; settings = {ATTRIBUTES = (Private, ); }; };
                EDEC98030AED7E170059137F /* WebCorePrefix.h in Headers */ = {isa = PBXBuildFile; fileRef = EDEC98020AED7E170059137F /* WebCorePrefix.h */; };
+               EFCC6C8F20FE914400A2321B /* CanvasActivityRecord.h in Headers */ = {isa = PBXBuildFile; fileRef = EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */; settings = {ATTRIBUTES = (Private, ); }; };
                F12171F516A8CED2000053CA /* WebVTTElement.cpp in Sources */ = {isa = PBXBuildFile; fileRef = F12171F316A8BC63000053CA /* WebVTTElement.cpp */; };
                F12171F616A8CF0B000053CA /* WebVTTElement.h in Headers */ = {isa = PBXBuildFile; fileRef = F12171F416A8BC63000053CA /* WebVTTElement.h */; };
                F344C7141125B82C00F26EEE /* InspectorFrontendClient.h in Headers */ = {isa = PBXBuildFile; fileRef = F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
                ED501DC50B249F2900AE18D9 /* EditorMac.mm */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.objcpp; path = EditorMac.mm; sourceTree = "<group>"; };
                EDE3A4FF0C7A430600956A37 /* ColorMac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ColorMac.h; sourceTree = "<group>"; };
                EDEC98020AED7E170059137F /* WebCorePrefix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = WebCorePrefix.h; sourceTree = "<group>"; tabWidth = 4; usesTabs = 0; };
+               EFB7287B2124C73D005C2558 /* CanvasActivityRecord.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = CanvasActivityRecord.cpp; sourceTree = "<group>"; };
+               EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CanvasActivityRecord.h; sourceTree = "<group>"; };
                F12171F316A8BC63000053CA /* WebVTTElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTElement.cpp; sourceTree = "<group>"; };
                F12171F416A8BC63000053CA /* WebVTTElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebVTTElement.h; sourceTree = "<group>"; };
                F344C7121125B82C00F26EEE /* InspectorFrontendClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorFrontendClient.h; sourceTree = "<group>"; };
                                93A1EAA20A5634D8006960A0 /* mac */,
                                63152D181F9531EE007A5E4B /* ApplicationManifestLoader.cpp */,
                                63152D171F9531EE007A5E4B /* ApplicationManifestLoader.h */,
+                               EFB7287B2124C73D005C2558 /* CanvasActivityRecord.cpp */,
+                               EFCC6C8D20FE914000A2321B /* CanvasActivityRecord.h */,
                                A149786C1ABAF33800CEF7E4 /* ContentFilter.cpp */,
                                A149786D1ABAF33800CEF7E4 /* ContentFilter.h */,
                                E1424C91164B52C800F32D40 /* CookieJar.cpp */,
                                7C1E8D011ED0C2DA00B1D983 /* CallbackResult.h in Headers */,
                                952076051F2675FE007D2AAB /* CallTracer.h in Headers */,
                                952076061F2675FE007D2AAB /* CallTracerTypes.h in Headers */,
+                               EFCC6C8F20FE914400A2321B /* CanvasActivityRecord.h in Headers */,
                                313171561FB079E5008D91FC /* CanvasBase.h in Headers */,
                                415CDAF51E6B8F8B004F11EE /* CanvasCaptureMediaStreamTrack.h in Headers */,
                                7C193BBB1F5E0EED0088F3E6 /* CanvasDirection.h in Headers */,
index 4d603b5..dc2bd7e 100644 (file)
@@ -34,6 +34,8 @@
 #include "FontCache.h"
 #include "FontCustomPlatformData.h"
 #include "FontDescription.h"
+#include "ResourceLoadObserver.h"
+#include "RuntimeEnabledFeatures.h"
 #include "SVGToOTFFontConversion.h"
 #include "SharedBuffer.h"
 
@@ -181,6 +183,10 @@ void CSSFontFaceSource::load(CSSFontSelector* fontSelector)
             fontDescription.setComputedSize(1);
             fontDescription.setShouldAllowUserInstalledFonts(m_face.allowUserInstalledFonts());
             success = FontCache::singleton().fontForFamily(fontDescription, m_familyNameOrURI, nullptr, nullptr, FontSelectionSpecifiedCapabilities(), true);
+            if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+                if (auto* document = fontSelector->document())
+                    ResourceLoadObserver::shared().logFontLoad(*document, m_familyNameOrURI.string(), success);
+            }
         }
         setStatus(success ? Status::Success : Status::Failure);
     }
index a2e5eb2..d55127b 100644 (file)
@@ -46,6 +46,8 @@
 #include "Frame.h"
 #include "FrameLoader.h"
 #include "Logging.h"
+#include "ResourceLoadObserver.h"
+#include "RuntimeEnabledFeatures.h"
 #include "Settings.h"
 #include "StyleProperties.h"
 #include "StyleResolver.h"
@@ -308,13 +310,21 @@ FontRanges CSSFontSelector::fontRangesForFamily(const FontDescription& fontDescr
 
     AtomicString familyForLookup = resolveGenericFamilyFirst ? resolveGenericFamily(m_document, fontDescription, familyName) : familyName;
     auto* face = m_cssFontFaceSet->fontFace(fontDescription.fontSelectionRequest(), familyForLookup);
-    if (!face) {
-        if (!resolveGenericFamilyFirst)
-            familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
-        return FontRanges(FontCache::singleton().fontForFamily(fontDescription, familyForLookup));
+    if (face) {
+        if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+            if (m_document)
+                ResourceLoadObserver::shared().logFontLoad(*m_document, familyForLookup.string(), true);
+        }
+        return face->fontRanges(fontDescription);
     }
-
-    return face->fontRanges(fontDescription);
+    if (!resolveGenericFamilyFirst)
+        familyForLookup = resolveGenericFamily(m_document, fontDescription, familyName);
+    auto font = FontCache::singleton().fontForFamily(fontDescription, familyForLookup);
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+        if (m_document)
+            ResourceLoadObserver::shared().logFontLoad(*m_document, familyForLookup.string(), !!font);
+    }
+    return FontRanges { WTFMove(font) };
 }
 
 void CSSFontSelector::clearDocument()
@@ -394,8 +404,12 @@ RefPtr<Font> CSSFontSelector::fallbackFontAt(const FontDescription& fontDescript
 
     if (!m_document->settings().fontFallbackPrefersPictographs())
         return nullptr;
-
-    return FontCache::singleton().fontForFamily(fontDescription, m_document->settings().pictographFontFamily());
+    auto& pictographFontFamily = m_document->settings().pictographFontFamily();
+    auto font = FontCache::singleton().fontForFamily(fontDescription, pictographFontFamily);
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logFontLoad(*m_document, pictographFontFamily.string(), !!font);
+    
+    return font;
 }
 
 }
index 6298643..4537bbb 100644 (file)
@@ -48,6 +48,7 @@
 #include "MIMETypeRegistry.h"
 #include "RenderElement.h"
 #include "RenderHTMLCanvas.h"
+#include "ResourceLoadObserver.h"
 #include "RuntimeEnabledFeatures.h"
 #include "ScriptController.h"
 #include "Settings.h"
@@ -698,6 +699,8 @@ ExceptionOr<UncachedString> HTMLCanvasElement::toDataURL(const String& mimeType,
 
     if (m_size.isEmpty() || !buffer())
         return UncachedString { "data:,"_s };
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logCanvasRead(document());
 
     auto encodingMIMEType = toEncodingMimeType(mimeType);
     auto quality = qualityFromJSValue(qualityValue);
@@ -727,6 +730,8 @@ ExceptionOr<void> HTMLCanvasElement::toBlob(ScriptExecutionContext& context, Ref
         callback->scheduleCallback(context, nullptr);
         return { };
     }
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logCanvasRead(document());
 
     auto encodingMIMEType = toEncodingMimeType(mimeType);
     auto quality = qualityFromJSValue(qualityValue);
@@ -755,8 +760,11 @@ ExceptionOr<void> HTMLCanvasElement::toBlob(ScriptExecutionContext& context, Ref
 RefPtr<ImageData> HTMLCanvasElement::getImageData()
 {
 #if ENABLE(WEBGL)
-    if (is<WebGLRenderingContextBase>(m_context.get()))
+    if (is<WebGLRenderingContextBase>(m_context.get())) {
+        if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+            ResourceLoadObserver::shared().logCanvasRead(document());
         return downcast<WebGLRenderingContextBase>(*m_context).paintRenderingResultsToImageData();
+    }
 #endif
     return nullptr;
 }
@@ -768,6 +776,8 @@ RefPtr<MediaSample> HTMLCanvasElement::toMediaSample()
     auto* imageBuffer = buffer();
     if (!imageBuffer)
         return nullptr;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logCanvasRead(document());
 
 #if PLATFORM(COCOA)
     makeRenderingResultsAvailable();
@@ -781,6 +791,8 @@ ExceptionOr<Ref<MediaStream>> HTMLCanvasElement::captureStream(ScriptExecutionCo
 {
     if (!originClean())
         return Exception(SecurityError, "Canvas is tainted"_s);
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logCanvasRead(document());
 
     if (frameRequestRate && frameRequestRate.value() < 0)
         return Exception(NotSupportedError, "frameRequestRate is negative"_s);
index e27d951..d15dec4 100644 (file)
@@ -41,6 +41,8 @@
 #include "InspectorInstrumentation.h"
 #include "Path2D.h"
 #include "RenderTheme.h"
+#include "ResourceLoadObserver.h"
+#include "RuntimeEnabledFeatures.h"
 #include "StyleProperties.h"
 #include "StyleResolver.h"
 #include "TextMetrics.h"
@@ -363,6 +365,12 @@ static void normalizeSpaces(String& text)
 
 Ref<TextMetrics> CanvasRenderingContext2D::measureText(const String& text)
 {
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+        auto& canvas = this->canvas();
+        ResourceLoadObserver::shared().logCanvasWriteOrMeasure(canvas.document(), text);
+        ResourceLoadObserver::shared().logCanvasRead(canvas.document());
+    }
+    
     Ref<TextMetrics> metrics = TextMetrics::create();
 
     String normalizedText = text;
@@ -451,6 +459,9 @@ FloatPoint CanvasRenderingContext2D::textOffset(float width, TextDirection direc
 
 void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, std::optional<float> maxWidth)
 {
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logCanvasWriteOrMeasure(this->canvas().document(), text);
+    
     auto& fontProxy = this->fontProxy();
     const auto& fontMetrics = fontProxy.fontMetrics();
 
diff --git a/Source/WebCore/loader/CanvasActivityRecord.cpp b/Source/WebCore/loader/CanvasActivityRecord.cpp
new file mode 100644 (file)
index 0000000..1bd789d
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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. AND ITS CONTRIBUTORS ``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 ITS 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 "CanvasActivityRecord.h"
+
+const unsigned maximumNumberOfStringsToRecord = 10;
+namespace WebCore {
+bool CanvasActivityRecord::recordWrittenOrMeasuredText(const String& text)
+{
+    // We limit the size of the textWritten HashSet to save memory and prevent bloating
+    // the plist with the resourceLoadStatistics entries. A few strings is often enough
+    // to provide sufficient information about the state of canvas activity.
+    if (textWritten.size() >= maximumNumberOfStringsToRecord)
+        return false;
+    return textWritten.add(text).isNewEntry;
+}
+
+void CanvasActivityRecord::mergeWith(const CanvasActivityRecord& otherCanvasActivityRecord)
+{
+    textWritten.add(otherCanvasActivityRecord.textWritten.begin(), otherCanvasActivityRecord.textWritten.end());
+    wasDataRead |= otherCanvasActivityRecord.wasDataRead;
+}
+} // namespace WebCore
diff --git a/Source/WebCore/loader/CanvasActivityRecord.h b/Source/WebCore/loader/CanvasActivityRecord.h
new file mode 100644 (file)
index 0000000..1e13f48
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2018 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. AND ITS CONTRIBUTORS ``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 ITS 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.
+ */
+
+#pragma once
+#include <wtf/HashSet.h>
+#include <wtf/text/StringHash.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+struct CanvasActivityRecord {
+    HashSet<String> textWritten;
+    bool wasDataRead { false };
+    
+    bool recordWrittenOrMeasuredText(const String&);
+    void mergeWith(const CanvasActivityRecord&);
+    
+    template <class Encoder> void encode(Encoder&) const;
+    template <class Decoder> static bool decode(Decoder&, CanvasActivityRecord&);
+};
+    
+template <class Encoder>
+void CanvasActivityRecord::encode(Encoder& encoder) const
+{
+    encoder << textWritten;
+    encoder << wasDataRead;
+}
+
+template <class Decoder>
+bool CanvasActivityRecord::decode(Decoder& decoder, CanvasActivityRecord& canvasActivityRecord)
+{
+    if (!decoder.decode(canvasActivityRecord.textWritten))
+        return false;
+    if (!decoder.decode(canvasActivityRecord.wasDataRead))
+        return false;
+    return true;
+}
+} // namespace WebCore
index 3735490..da17102 100644 (file)
@@ -46,6 +46,7 @@
 #include "LoadTiming.h"
 #include "LoaderStrategy.h"
 #include "Performance.h"
+#include "PlatformStrategies.h"
 #include "ProgressTracker.h"
 #include "ResourceError.h"
 #include "ResourceRequest.h"
index d7318b1..6ab6855 100644 (file)
 #include "UserGestureIndicator.h"
 #include "WindowFeatures.h"
 #include "XMLDocumentParser.h"
+#include <dom/ScriptDisallowedScope.h>
 #include <wtf/CompletionHandler.h>
 #include <wtf/Ref.h>
 #include <wtf/SetForScope.h>
index 8d03ba0..467e269 100644 (file)
@@ -36,6 +36,7 @@
 #include "ResourceLoadStatistics.h"
 #include "ResourceRequest.h"
 #include "ResourceResponse.h"
+#include "RuntimeEnabledFeatures.h"
 #include "ScriptExecutionContext.h"
 #include "SecurityOrigin.h"
 #include "Settings.h"
@@ -242,6 +243,91 @@ void ResourceLoadObserver::requestStorageAccessUnderOpener(const String& domainI
 }
 #endif
 
+void ResourceLoadObserver::logFontLoad(const Document& document, const String& familyName, bool loadStatus)
+{
+    if (!shouldLog(document.sessionID().isEphemeral()))
+        return;
+    auto registrableDomain = primaryDomain(document.url());
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(registrableDomain);
+    bool shouldCallNotificationCallback = false;
+    if (!loadStatus) {
+        if (statistics.fontsFailedToLoad.add(familyName).isNewEntry)
+            shouldCallNotificationCallback = true;
+    } else {
+        if (statistics.fontsSuccessfullyLoaded.add(familyName).isNewEntry)
+            shouldCallNotificationCallback = true;
+    }
+    auto mainFrameRegistrableDomain = primaryDomain(document.topDocument().url());
+    if (statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs.add(mainFrameRegistrableDomain).isNewEntry)
+        shouldCallNotificationCallback = true;
+    if (shouldCallNotificationCallback)
+        scheduleNotificationIfNeeded();
+}
+    
+void ResourceLoadObserver::logCanvasRead(const Document& document)
+{
+    if (!shouldLog(document.sessionID().isEphemeral()))
+        return;
+    auto registrableDomain = primaryDomain(document.url());
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(registrableDomain);
+    auto mainFrameRegistrableDomain = primaryDomain(document.topDocument().url());
+    statistics.canvasActivityRecord.wasDataRead = true;
+    if (statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs.add(mainFrameRegistrableDomain).isNewEntry)
+        scheduleNotificationIfNeeded();
+}
+
+void ResourceLoadObserver::logCanvasWriteOrMeasure(const Document& document, const String& textWritten)
+{
+    if (!shouldLog(document.sessionID().isEphemeral()))
+        return;
+    auto registrableDomain = primaryDomain(document.url());
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(registrableDomain);
+    bool shouldCallNotificationCallback = false;
+    auto mainFrameRegistrableDomain = primaryDomain(document.topDocument().url());
+    if (statistics.canvasActivityRecord.recordWrittenOrMeasuredText(textWritten))
+        shouldCallNotificationCallback = true;
+    if (statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs.add(mainFrameRegistrableDomain).isNewEntry)
+        shouldCallNotificationCallback = true;
+    if (shouldCallNotificationCallback)
+        scheduleNotificationIfNeeded();
+}
+    
+void ResourceLoadObserver::logNavigatorAPIAccessed(const Document& document, const ResourceLoadStatistics::NavigatorAPI functionName)
+{
+    if (!shouldLog(document.sessionID().isEphemeral()))
+        return;
+    auto registrableDomain = primaryDomain(document.url());
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(registrableDomain);
+    bool shouldCallNotificationCallback = false;
+    if (!statistics.navigatorFunctionsAccessed.contains(functionName)) {
+        statistics.navigatorFunctionsAccessed.add(functionName);
+        shouldCallNotificationCallback = true;
+    }
+    auto mainFrameRegistrableDomain = primaryDomain(document.topDocument().url());
+    if (statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs.add(mainFrameRegistrableDomain).isNewEntry)
+        shouldCallNotificationCallback = true;
+    if (shouldCallNotificationCallback)
+        scheduleNotificationIfNeeded();
+}
+    
+void ResourceLoadObserver::logScreenAPIAccessed(const Document& document, const ResourceLoadStatistics::ScreenAPI functionName)
+{
+    if (!shouldLog(document.sessionID().isEphemeral()))
+        return;
+    auto registrableDomain = primaryDomain(document.url());
+    auto& statistics = ensureResourceStatisticsForPrimaryDomain(registrableDomain);
+    bool shouldCallNotificationCallback = false;
+    if (!statistics.screenFunctionsAccessed.contains(functionName)) {
+        statistics.screenFunctionsAccessed.add(functionName);
+        shouldCallNotificationCallback = true;
+    }
+    auto mainFrameRegistrableDomain = primaryDomain(document.topDocument().url());
+    if (statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs.add(mainFrameRegistrableDomain).isNewEntry)
+        shouldCallNotificationCallback = true;
+    if (shouldCallNotificationCallback)
+        scheduleNotificationIfNeeded();
+}
+    
 ResourceLoadStatistics& ResourceLoadObserver::ensureResourceStatisticsForPrimaryDomain(const String& primaryDomain)
 {
     auto addResult = m_resourceStatisticsMap.ensure(primaryDomain, [&primaryDomain] {
index 5b6d918..5f051bb 100644 (file)
@@ -25,6 +25,8 @@
 
 #pragma once
 
+#include "CanvasActivityRecord.h"
+#include "ResourceLoadStatistics.h"
 #include "Timer.h"
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
@@ -58,6 +60,12 @@ public:
     void logWebSocketLoading(const URL& targetURL, const URL& mainFrameURL, bool usesEphemeralSession);
     void logUserInteractionWithReducedTimeResolution(const Document&);
     void logWindowCreation(const URL& popupUrl, uint64_t openerPageID, Document& openerDocument);
+    
+    void logFontLoad(const Document&, const String& familyName, bool loadStatus);
+    void logCanvasRead(const Document&);
+    void logCanvasWriteOrMeasure(const Document&, const String& textWritten);
+    void logNavigatorAPIAccessed(const Document&, const ResourceLoadStatistics::NavigatorAPI);
+    void logScreenAPIAccessed(const Document&, const ResourceLoadStatistics::ScreenAPI);
 
     WEBCORE_EXPORT String statisticsForOrigin(const String&);
 
index 9e2b7cf..32596d1 100644 (file)
@@ -29,6 +29,7 @@
 #include "KeyedCoding.h"
 #include "PublicSuffix.h"
 #include <wtf/MainThread.h>
+#include <wtf/text/ASCIILiteral.h>
 #include <wtf/text/StringBuilder.h>
 #include <wtf/text/StringHash.h>
 
@@ -49,13 +50,43 @@ static void encodeHashCountedSet(KeyedEncoder& encoder, const String& label, con
     });
 }
 
-static void encodeHashSet(KeyedEncoder& encoder, const String& label, const HashSet<String>& hashSet)
+static void encodeHashSet(KeyedEncoder& encoder, const String& label,  const String& key, const HashSet<String>& hashSet)
 {
     if (hashSet.isEmpty())
         return;
     
-    encoder.encodeObjects(label, hashSet.begin(), hashSet.end(), [](KeyedEncoder& encoderInner, const String& origin) {
-        encoderInner.encodeString("origin", origin);
+    encoder.encodeObjects(label, hashSet.begin(), hashSet.end(), [&key](KeyedEncoder& encoderInner, const String& origin) {
+        encoderInner.encodeString(key, origin);
+    });
+}
+
+static void encodeOriginHashSet(KeyedEncoder& encoder, const String& label, const HashSet<String>& hashSet)
+{
+    encodeHashSet(encoder, label, "origin", hashSet);
+}
+
+template<typename T>
+static void encodeOptionSet(KeyedEncoder& encoder, const String& label, const OptionSet<T>& optionSet)
+{
+    if (optionSet.isEmpty())
+        return;
+    
+    uint64_t optionSetBitMask = optionSet.toRaw();
+    encoder.encodeUInt64(label, optionSetBitMask);
+}
+    
+static void encodeFontHashSet(KeyedEncoder& encoder, const String& label, const HashSet<String>& hashSet)
+{
+    encodeHashSet(encoder, label, "font", hashSet);
+}
+    
+static void encodeCanvasActivityRecord(KeyedEncoder& encoder, const String& label, const CanvasActivityRecord& canvasActivityRecord)
+{
+    encoder.encodeObject(label, canvasActivityRecord, [] (KeyedEncoder& encoderInner, const CanvasActivityRecord& canvasActivityRecord) {
+        encoderInner.encodeBool("wasDataRead", canvasActivityRecord.wasDataRead);
+        encoderInner.encodeObjects("textWritten", canvasActivityRecord.textWritten.begin(), canvasActivityRecord.textWritten.end(), [] (KeyedEncoder& encoderInner2, const String& text) {
+            encoderInner2.encodeString("text", text);
+        });
     });
 }
 
@@ -71,7 +102,7 @@ void ResourceLoadStatistics::encode(KeyedEncoder& encoder) const
     encoder.encodeBool("grandfathered", grandfathered);
 
     // Storage access
-    encodeHashSet(encoder, "storageAccessUnderTopFrameOrigins", storageAccessUnderTopFrameOrigins);
+    encodeOriginHashSet(encoder, "storageAccessUnderTopFrameOrigins", storageAccessUnderTopFrameOrigins);
 
     // Top frame stats
     encodeHashCountedSet(encoder, "topFrameUniqueRedirectsTo", topFrameUniqueRedirectsTo);
@@ -92,6 +123,13 @@ void ResourceLoadStatistics::encode(KeyedEncoder& encoder) const
 
     encoder.encodeUInt32("timesAccessedAsFirstPartyDueToUserInteraction", timesAccessedAsFirstPartyDueToUserInteraction);
     encoder.encodeUInt32("timesAccessedAsFirstPartyDueToStorageAccessAPI", timesAccessedAsFirstPartyDueToStorageAccessAPI);
+    
+    encodeFontHashSet(encoder, "fontsFailedToLoad", fontsFailedToLoad);
+    encodeFontHashSet(encoder, "fontsSuccessfullyLoaded", fontsSuccessfullyLoaded);
+    encodeHashCountedSet(encoder, "topFrameRegistrableDomainsWhichAccessedWebAPIs", topFrameRegistrableDomainsWhichAccessedWebAPIs);
+    encodeCanvasActivityRecord(encoder, "canvasActivityRecord", canvasActivityRecord);
+    encodeOptionSet(encoder, "navigatorFunctionsAccessedBitMask", navigatorFunctionsAccessed);
+    encodeOptionSet(encoder, "screenFunctionsAccessedBitMask", screenFunctionsAccessed);
 }
 
 static void decodeHashCountedSet(KeyedDecoder& decoder, const String& label, HashCountedSet<String>& hashCountedSet)
@@ -110,11 +148,11 @@ static void decodeHashCountedSet(KeyedDecoder& decoder, const String& label, Has
     });
 }
 
-static void decodeHashSet(KeyedDecoder& decoder, const String& label, HashSet<String>& hashSet)
+static void decodeHashSet(KeyedDecoder& decoder, const String& label, const String& key, HashSet<String>& hashSet)
 {
     Vector<String> ignore;
-    decoder.decodeObjects(label, ignore, [&hashSet](KeyedDecoder& decoderInner, String& origin) {
-        if (!decoderInner.decodeString("origin", origin))
+    decoder.decodeObjects(label, ignore, [&hashSet, &key](KeyedDecoder& decoderInner, String& origin) {
+        if (!decoderInner.decodeString(key, origin))
             return false;
         
         hashSet.add(origin);
@@ -122,6 +160,40 @@ static void decodeHashSet(KeyedDecoder& decoder, const String& label, HashSet<St
     });
 }
 
+static void decodeOriginHashSet(KeyedDecoder& decoder, const String& label, HashSet<String>& hashSet)
+{
+    decodeHashSet(decoder, label, "origin", hashSet);
+}
+
+template<typename T>
+static void decodeOptionSet(KeyedDecoder& decoder, const String& label, OptionSet<T>& optionSet)
+{
+    uint64_t optionSetBitMask = 0;
+    decoder.decodeUInt64(label, optionSetBitMask);
+    optionSet = OptionSet<T>::fromRaw(optionSetBitMask);
+}
+    
+static void decodeFontHashSet(KeyedDecoder& decoder, const String& label, HashSet<String>& hashSet)
+{
+    decodeHashSet(decoder, label, "font", hashSet);
+}
+    
+static void decodeCanvasActivityRecord(KeyedDecoder& decoder, const String& label, CanvasActivityRecord& canvasActivityRecord)
+{
+    decoder.decodeObject(label, canvasActivityRecord, [] (KeyedDecoder& decoderInner, CanvasActivityRecord& canvasActivityRecord) {
+        if (!decoderInner.decodeBool("wasDataRead", canvasActivityRecord.wasDataRead))
+            return false;
+        Vector<String> ignore;
+        decoderInner.decodeObjects("textWritten", ignore, [&canvasActivityRecord] (KeyedDecoder& decoderInner2, String& text) {
+            if (!decoderInner2.decodeString("text", text))
+                return false;
+            canvasActivityRecord.textWritten.add(text);
+            return true;
+        });
+        return true;
+    });
+}
+
 bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, unsigned modelVersion)
 {
     if (!decoder.decodeString("PrevalentResourceOrigin", highLevelDomain))
@@ -132,7 +204,7 @@ bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, unsigned modelVersion
         return false;
 
     // Storage access
-    decodeHashSet(decoder, "storageAccessUnderTopFrameOrigins", storageAccessUnderTopFrameOrigins);
+    decodeOriginHashSet(decoder, "storageAccessUnderTopFrameOrigins", storageAccessUnderTopFrameOrigins);
 
     // Top frame stats
     if (modelVersion >= 11) {
@@ -180,6 +252,16 @@ bool ResourceLoadStatistics::decode(KeyedDecoder& decoder, unsigned modelVersion
         if (!decoder.decodeUInt32("timesAccessedAsFirstPartyDueToStorageAccessAPI", timesAccessedAsFirstPartyDueToStorageAccessAPI))
             timesAccessedAsFirstPartyDueToStorageAccessAPI = 0;
     }
+    
+    if (modelVersion >= 13) {
+        decodeFontHashSet(decoder, "fontsFailedToLoad", fontsFailedToLoad);
+        decodeFontHashSet(decoder, "fontsSuccessfullyLoaded", fontsSuccessfullyLoaded);
+        decodeHashCountedSet(decoder, "topFrameRegistrableDomainsWhichAccessedWebAPIs", topFrameRegistrableDomainsWhichAccessedWebAPIs);
+        decodeCanvasActivityRecord(decoder, "canvasActivityRecord", canvasActivityRecord);
+        decodeOptionSet(decoder, "navigatorFunctionsAccessedBitMask", navigatorFunctionsAccessed);
+        decodeOptionSet(decoder, "screenFunctionsAccessedBitMask", screenFunctionsAccessed);
+    }
+    
     return true;
 }
 
@@ -225,11 +307,81 @@ static void appendHashSet(StringBuilder& builder, const String& label, const Has
     }
 }
 
+static ASCIILiteral navigatorAPIEnumToString(ResourceLoadStatistics::NavigatorAPI navigatorEnum)
+{
+    switch (navigatorEnum) {
+    case ResourceLoadStatistics::NavigatorAPI::JavaEnabled:
+        return "javaEnabled"_s;
+    case ResourceLoadStatistics::NavigatorAPI::MimeTypes:
+        return "mimeTypes"_s;
+    case ResourceLoadStatistics::NavigatorAPI::CookieEnabled:
+        return "cookieEnabled"_s;
+    case ResourceLoadStatistics::NavigatorAPI::Plugins:
+        return "plugins"_s;
+    case ResourceLoadStatistics::NavigatorAPI::UserAgent:
+        return "userAgent"_s;
+    case ResourceLoadStatistics::NavigatorAPI::AppVersion:
+        return "appVersion"_s;
+    }
+    ASSERT_NOT_REACHED();
+    return "Invalid navigator API"_s;
+}
+
+static ASCIILiteral screenAPIEnumToString(ResourceLoadStatistics::ScreenAPI screenEnum)
+{
+    switch (screenEnum) {
+    case ResourceLoadStatistics::ScreenAPI::Height:
+        return "height"_s;
+    case ResourceLoadStatistics::ScreenAPI::Width:
+        return "width"_s;
+    case ResourceLoadStatistics::ScreenAPI::ColorDepth:
+        return "colorDepth"_s;
+    case ResourceLoadStatistics::ScreenAPI::PixelDepth:
+        return "pixelDepth"_s;
+    case ResourceLoadStatistics::ScreenAPI::AvailLeft:
+        return "availLeft"_s;
+    case ResourceLoadStatistics::ScreenAPI::AvailTop:
+        return "availTop"_s;
+    case ResourceLoadStatistics::ScreenAPI::AvailHeight:
+        return "availHeight"_s;
+    case ResourceLoadStatistics::ScreenAPI::AvailWidth:
+        return "availWidth"_s;
+    }
+    ASSERT_NOT_REACHED();
+    return "Invalid screen API"_s;
+}
+    
+static void appendNavigatorAPIOptionSet(StringBuilder& builder, const OptionSet<ResourceLoadStatistics::NavigatorAPI>& optionSet)
+{
+    if (optionSet.isEmpty())
+        return;
+    builder.appendLiteral("    navigatorFunctionsAccessed:\n");
+    for (auto navigatorAPI : optionSet) {
+        builder.appendLiteral("        ");
+        builder.append(navigatorAPIEnumToString(navigatorAPI).characters());
+        builder.append('\n');
+    }
+}
+    
+static void appendScreenAPIOptionSet(StringBuilder& builder, const OptionSet<ResourceLoadStatistics::ScreenAPI>& optionSet)
+{
+    if (optionSet.isEmpty())
+        return;
+    builder.appendLiteral("    screenFunctionsAccessed:\n");
+    for (auto screenAPI : optionSet) {
+        builder.appendLiteral("        ");
+        builder.append(screenAPIEnumToString(screenAPI).characters());
+        builder.append('\n');
+    }
+}
+
 String ResourceLoadStatistics::toString() const
 {
     StringBuilder builder;
-    
-    builder.appendLiteral("lastSeen");
+    builder.appendLiteral("High level domain: ");
+    builder.append(highLevelDomain);
+    builder.append('\n');
+    builder.appendLiteral("    lastSeen: ");
     builder.appendNumber(lastSeen.secondsSinceEpoch().value());
     builder.append('\n');
     
@@ -239,7 +391,7 @@ String ResourceLoadStatistics::toString() const
     builder.appendLiteral("    mostRecentUserInteraction: ");
     builder.appendNumber(mostRecentUserInteractionTime.secondsSinceEpoch().value());
     builder.append('\n');
-    appendBoolean(builder, "    grandfathered", grandfathered);
+    appendBoolean(builder, "grandfathered", grandfathered);
     builder.append('\n');
 
     // Storage access
@@ -259,7 +411,9 @@ String ResourceLoadStatistics::toString() const
 
     // Prevalent Resource
     appendBoolean(builder, "isPrevalentResource", isPrevalentResource);
-    appendBoolean(builder, "    isVeryPrevalentResource", isVeryPrevalentResource);
+    builder.append('\n');
+    appendBoolean(builder, "isVeryPrevalentResource", isVeryPrevalentResource);
+    builder.append('\n');
     builder.appendLiteral("    dataRecordsRemoved: ");
     builder.appendNumber(dataRecordsRemoved);
     builder.append('\n');
@@ -268,6 +422,14 @@ String ResourceLoadStatistics::toString() const
     appendBoolean(builder, "isMarkedForCookieBlocking", isMarkedForCookieBlocking);
     builder.append('\n');
 
+    appendHashSet(builder, "fontsFailedToLoad", fontsFailedToLoad);
+    appendHashSet(builder, "fontsSuccessfullyLoaded", fontsSuccessfullyLoaded);
+    appendHashCountedSet(builder, "topFrameRegistrableDomainsWhichAccessedWebAPIs", topFrameRegistrableDomainsWhichAccessedWebAPIs);
+    appendNavigatorAPIOptionSet(builder, navigatorFunctionsAccessed);
+    appendScreenAPIOptionSet(builder, screenFunctionsAccessed);
+    appendHashSet(builder, "canvasTextWritten", canvasActivityRecord.textWritten);
+    appendBoolean(builder, "canvasReadData", canvasActivityRecord.wasDataRead);
+    builder.append('\n');
     builder.append('\n');
 
     return builder.toString();
@@ -330,6 +492,13 @@ void ResourceLoadStatistics::merge(const ResourceLoadStatistics& other)
     
     // In-memory only
     isMarkedForCookieBlocking |= other.isMarkedForCookieBlocking;
+
+    mergeHashSet(fontsFailedToLoad, other.fontsFailedToLoad);
+    mergeHashSet(fontsSuccessfullyLoaded, other.fontsSuccessfullyLoaded);
+    mergeHashCountedSet(topFrameRegistrableDomainsWhichAccessedWebAPIs, other.topFrameRegistrableDomainsWhichAccessedWebAPIs);
+    canvasActivityRecord.mergeWith(other.canvasActivityRecord);
+    navigatorFunctionsAccessed.add(other.navigatorFunctionsAccessed);
+    screenFunctionsAccessed.add(other.screenFunctionsAccessed);
 }
 
 String ResourceLoadStatistics::primaryDomain(const URL& url)
index 27a17ff..f94d6fd 100644 (file)
 
 #pragma once
 
+#include "CanvasActivityRecord.h"
 #include "URL.h"
 #include <wtf/HashCountedSet.h>
 #include <wtf/HashSet.h>
+#include <wtf/OptionSet.h>
 #include <wtf/WallTime.h>
 #include <wtf/text/StringHash.h>
 #include <wtf/text/WTFString.h>
@@ -96,6 +98,33 @@ struct ResourceLoadStatistics {
 
     // In-memory only
     bool isMarkedForCookieBlocking { false };
+    
+    // This set represents the registrable domain of the top frame where web API
+    // were used in the top frame or one of its subframes.
+    HashCountedSet<String> topFrameRegistrableDomainsWhichAccessedWebAPIs;
+    HashSet<String> fontsFailedToLoad;
+    HashSet<String> fontsSuccessfullyLoaded;
+    CanvasActivityRecord canvasActivityRecord;
+    enum class NavigatorAPI : uint64_t {
+        AppVersion = 1 << 0,
+        UserAgent = 1 << 1,
+        Plugins = 1 << 2,
+        MimeTypes = 1 << 3,
+        CookieEnabled = 1 << 4,
+        JavaEnabled = 1 << 5,
+    };
+    enum class ScreenAPI : uint64_t {
+        Height = 1 << 0,
+        Width = 1 << 1,
+        ColorDepth = 1 << 2,
+        PixelDepth = 1 << 3,
+        AvailLeft = 1 << 4,
+        AvailTop = 1 << 5,
+        AvailHeight = 1 << 6,
+        AvailWidth = 1 << 7,
+    };
+    OptionSet<NavigatorAPI> navigatorFunctionsAccessed;
+    OptionSet<ScreenAPI> screenFunctionsAccessed;
 };
 
 } // namespace WebCore
index a6ab680..4c25981 100644 (file)
 
 #include "CachedResource.h"
 #include "PerformanceServerTiming.h"
+#include "RuntimeEnabledFeatures.h"
 #include "SecurityOrigin.h"
 #include "ServerTimingParser.h"
+#include <wtf/CrossThreadCopier.h>
 
 namespace WebCore {
 
index 7569ca8..21f9e3e 100644 (file)
@@ -37,6 +37,8 @@
 #include "Page.h"
 #include "PlatformStrategies.h"
 #include "PluginData.h"
+#include "ResourceLoadObserver.h"
+#include "RuntimeEnabledFeatures.h"
 #include "ScriptController.h"
 #include "SecurityOrigin.h"
 #include "Settings.h"
@@ -44,7 +46,6 @@
 #include <wtf/StdLibExtras.h>
 #include <wtf/WeakPtr.h>
 
-
 namespace WebCore {
 using namespace WTF;
 
@@ -74,6 +75,8 @@ String Navigator::appVersion() const
 {
     if (!m_frame)
         return String();
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::AppVersion);
     String appVersion = NavigatorBase::appVersion();
     if (shouldHideFourDot(*m_frame))
         appVersion.replace("4.", "4_");
@@ -82,7 +85,11 @@ String Navigator::appVersion() const
 
 const String& Navigator::userAgent() const
 {
-    if (m_userAgent.isNull() && m_frame && m_frame->page())
+    if (!m_frame || !m_frame->page())
+        return m_userAgent;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::UserAgent);
+    if (m_userAgent.isNull())
         m_userAgent = m_frame->loader().userAgent(m_frame->document()->url());
     return m_userAgent;
 }
@@ -136,6 +143,10 @@ void Navigator::share(ScriptExecutionContext& context, ShareData data, Ref<Defer
 
 DOMPluginArray& Navigator::plugins()
 {
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+        if (m_frame)
+            ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::Plugins);
+    }
     if (!m_plugins)
         m_plugins = DOMPluginArray::create(m_frame);
     return *m_plugins;
@@ -143,6 +154,10 @@ DOMPluginArray& Navigator::plugins()
 
 DOMMimeTypeArray& Navigator::mimeTypes()
 {
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled()) {
+        if (m_frame)
+            ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::MimeTypes);
+    }
     if (!m_mimeTypes)
         m_mimeTypes = DOMMimeTypeArray::create(m_frame);
     return *m_mimeTypes;
@@ -153,6 +168,9 @@ bool Navigator::cookieEnabled() const
     if (!m_frame)
         return false;
 
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::CookieEnabled);
+
     if (m_frame->page() && !m_frame->page()->settings().cookieEnabled())
         return false;
 
@@ -168,6 +186,9 @@ bool Navigator::javaEnabled() const
     if (!m_frame)
         return false;
 
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logNavigatorAPIAccessed(*m_frame->document(), ResourceLoadStatistics::NavigatorAPI::JavaEnabled);
+
     if (!m_frame->settings().isJavaEnabled())
         return false;
     if (m_frame->document()->securityOrigin().isLocal() && !m_frame->settings().isJavaEnabledForLocalFiles())
index ff487d7..b20abe4 100644 (file)
@@ -33,6 +33,8 @@
 #include "Frame.h"
 #include "FrameView.h"
 #include "PlatformScreen.h"
+#include "ResourceLoadObserver.h"
+#include "RuntimeEnabledFeatures.h"
 
 namespace WebCore {
 
@@ -45,6 +47,8 @@ unsigned Screen::height() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::Height);
     long height = static_cast<long>(screenRect(m_frame->view()).height());
     return static_cast<unsigned>(height);
 }
@@ -53,6 +57,8 @@ unsigned Screen::width() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::Width);
     long width = static_cast<long>(screenRect(m_frame->view()).width());
     return static_cast<unsigned>(width);
 }
@@ -61,6 +67,8 @@ unsigned Screen::colorDepth() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::ColorDepth);
     return static_cast<unsigned>(screenDepth(m_frame->view()));
 }
 
@@ -68,6 +76,8 @@ unsigned Screen::pixelDepth() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::PixelDepth);
     return static_cast<unsigned>(screenDepth(m_frame->view()));
 }
 
@@ -75,6 +85,8 @@ int Screen::availLeft() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::AvailLeft);
     return static_cast<int>(screenAvailableRect(m_frame->view()).x());
 }
 
@@ -82,6 +94,8 @@ int Screen::availTop() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::AvailTop);
     return static_cast<int>(screenAvailableRect(m_frame->view()).y());
 }
 
@@ -89,6 +103,8 @@ unsigned Screen::availHeight() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::AvailHeight);
     return static_cast<unsigned>(screenAvailableRect(m_frame->view()).height());
 }
 
@@ -96,6 +112,8 @@ unsigned Screen::availWidth() const
 {
     if (!m_frame)
         return 0;
+    if (RuntimeEnabledFeatures::sharedFeatures().webAPIStatisticsEnabled())
+        ResourceLoadObserver::shared().logScreenAPIAccessed(*m_frame->document(), ResourceLoadStatistics::ScreenAPI::AvailWidth);
     return static_cast<unsigned>(screenAvailableRect(m_frame->view()).width());
 }
 
index e60abee..ef4d3e4 100644 (file)
@@ -1 +1,14 @@
+2018-09-11  Woodrow Wang  <woodrow_wang@apple.com>
+
+        Add Web API Statistics Collection
+        https://bugs.webkit.org/show_bug.cgi?id=187773
+        <rdar://problem/44155162>
+
+        Reviewed by Brent Fulgham.
+
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<ResourceLoadStatistics>::encode):
+        (IPC::ArgumentCoder<ResourceLoadStatistics>::decode):
+        * UIProcess/ResourceLoadStatisticsMemoryStore.cpp:
+
 == Rolled over to ChangeLog-2018-09-11 ==
index 9dab37d..8860592 100644 (file)
@@ -2664,6 +2664,16 @@ void ArgumentCoder<ResourceLoadStatistics>::encode(Encoder& encoder, const WebCo
     encoder << statistics.isPrevalentResource;
     encoder << statistics.isVeryPrevalentResource;
     encoder << statistics.dataRecordsRemoved;
+    
+    encoder << statistics.fontsFailedToLoad;
+    encoder << statistics.fontsSuccessfullyLoaded;
+    encoder << statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs;
+    
+    encoder << statistics.canvasActivityRecord;
+    
+    encoder << statistics.navigatorFunctionsAccessed;
+    encoder << statistics.screenFunctionsAccessed;
+    
 }
 
 std::optional<ResourceLoadStatistics> ArgumentCoder<ResourceLoadStatistics>::decode(Decoder& decoder)
@@ -2723,7 +2733,25 @@ std::optional<ResourceLoadStatistics> ArgumentCoder<ResourceLoadStatistics>::dec
     
     if (!decoder.decode(statistics.dataRecordsRemoved))
         return std::nullopt;
-
+    
+    if (!decoder.decode(statistics.fontsFailedToLoad))
+        return std::nullopt;
+    
+    if (!decoder.decode(statistics.fontsSuccessfullyLoaded))
+        return std::nullopt;
+    
+    if (!decoder.decode(statistics.topFrameRegistrableDomainsWhichAccessedWebAPIs))
+        return std::nullopt;
+    
+    if (!decoder.decode(statistics.canvasActivityRecord))
+        return std::nullopt;
+    
+    if (!decoder.decode(statistics.navigatorFunctionsAccessed))
+        return std::nullopt;
+    
+    if (!decoder.decode(statistics.screenFunctionsAccessed))
+        return std::nullopt;
+    
     return WTFMove(statistics);
 }
 
index b545adf..14c837a 100644 (file)
@@ -46,7 +46,7 @@
 namespace WebKit {
 using namespace WebCore;
 
-constexpr unsigned statisticsModelVersion { 12 };
+constexpr unsigned statisticsModelVersion { 13 };
 constexpr unsigned maxNumberOfRecursiveCallsInRedirectTraceBack { 50 };
 constexpr Seconds minimumStatisticsProcessingInterval { 5_s };
 constexpr unsigned operatingDatesWindow { 30 };