Add deprecation message for AppCache in insecure contexts

This adds a deprecation messages for uses of the manifest attribute or
use of the AppCache API in insecure contexts. Adds LayoutTests as well
to confirm warning messages. Additionally, adds counts and RAPPOR stats
to track usage.

BUG=588931

Review URL: https://codereview.chromium.org/1723353002

Cr-Commit-Position: refs/heads/master@{#377463}
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin-expected.txt
index 856190a..714d8781 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Use of the Application Cache is deprecated on insecure origins. Support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.
 CONSOLE WARNING: The devicemotion event is deprecated on insecure origins, and support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.
 CONSOLE WARNING: The deviceorientation event is deprecated on insecure origins, and support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.
 CONSOLE WARNING: getCurrentPosition() and watchPosition() are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.
@@ -11,5 +12,6 @@
 PASS watchPosition 
 PASS navigator.webkitGetUserMedia 
 PASS navigator.mediaDevices.getUserMedia 
+PASS appcache 
 Harness: the test ran to completion.
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html
index 31cd796..fc366bd 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/old-powerful-features-on-insecure-origin.html
@@ -1,5 +1,5 @@
 <!DOCTYPE html>
-<html>
+<html manifest="/security/powerfulFeatureRestrictions/resources/simple.manifest">
 <head>
 <title>Old Powerful Features on an Insecure Origin</title>
 </head>
@@ -80,6 +80,22 @@
                 assert_equals(error.message, "Only secure origins are allowed (see: https://goo.gl/Y0ZkNV).");
             });
     }, 'navigator.mediaDevices.getUserMedia');
+
+    async_test(function() {
+        var cached = this.step_func(function() {
+            var test = this;
+            fetch("/security/powerfulFeatureRestrictions/resources/simple.txt")
+                .then(this.step_func(function(response) {
+                    assert_equals(response.status, 200);
+                    response.text().then(this.step_func_done(function(data) {
+                        assert_equals(data, "Hello, World!");
+                    }));
+                }))
+                .catch(this.unreached_func("fetch() for cachable resource unexpectedly failed"));
+        });
+
+        applicationCache.addEventListener('cached', cached, false);
+    }, 'appcache');
 }
 </script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.manifest b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.manifest
new file mode 100644
index 0000000..4f36a67
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.manifest
@@ -0,0 +1,5 @@
+CACHE MANIFEST
+/security/powerfulFeatureRestrictions/resources/simple.txt
+
+NETWORK:
+*
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.txt b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.txt
new file mode 100644
index 0000000..b45ef6fe
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/powerfulFeatureRestrictions/resources/simple.txt
@@ -0,0 +1 @@
+Hello, World!
\ No newline at end of file
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp
index 2b4fe929..c8ed596 100644
--- a/third_party/WebKit/Source/core/frame/Deprecation.cpp
+++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -270,6 +270,10 @@
     case UseCounter::EncryptedMediaInsecureOrigin:
         return "requestMediaKeySystemAccess() is deprecated on insecure origins in the specification. Support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.";
 
+    case UseCounter::ApplicationCacheManifestSelectInsecureOrigin:
+    case UseCounter::ApplicationCacheAPIInsecureOrigin:
+        return "Use of the Application Cache is deprecated on insecure origins. Support will be removed in the future. You should consider switching your application to a secure origin, such as HTTPS. See https://goo.gl/rStTGz for more details.";
+
     case UseCounter::ElementCreateShadowRootMultiple:
         return "Calling Element.createShadowRoot() for an element which already hosts a shadow root is deprecated. See https://www.chromestatus.com/features/4668884095336448 for more details.";
 
diff --git a/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.cpp b/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.cpp
index 0b3ec59f..696e8df 100644
--- a/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.cpp
+++ b/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.cpp
@@ -158,6 +158,10 @@
         Platform::current()->recordRappor("PowerfulFeatureUse.Host.GetUserMedia.Insecure", origin);
     if (get(Feature::GetUserMediaSecureOrigin))
         Platform::current()->recordRappor("PowerfulFeatureUse.Host.GetUserMedia.Secure", origin);
+    if (get(Feature::ApplicationCacheManifestSelectInsecureOrigin))
+        Platform::current()->recordRappor("PowerfulFeatureUse.Host.ApplicationCacheManifestSelect.Insecure", origin);
+    if (get(Feature::ApplicationCacheAPIInsecureOrigin))
+        Platform::current()->recordRappor("PowerfulFeatureUse.Host.ApplicationCacheAPI.Insecure", origin);
 }
 
 void OriginsUsingFeatures::Value::recordNameToRappor(const String& name)
diff --git a/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.h b/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.h
index 4e8e449..36b9c86 100644
--- a/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.h
+++ b/third_party/WebKit/Source/core/frame/OriginsUsingFeatures.h
@@ -35,6 +35,8 @@
         GetUserMediaInsecureOrigin,
         GetUserMediaSecureOrigin,
         ElementAttachShadow,
+        ApplicationCacheManifestSelectInsecureOrigin,
+        ApplicationCacheAPIInsecureOrigin,
 
         NumberOfFeatures // This must be the last item.
     };
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index afd4e589..89239e6 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1082,6 +1082,10 @@
         DocumentCreateEventInputEvent = 1242,
         WebAnimationHyphenatedProperty = 1243,
         FormControlsCollectionReturnsRadioNodeListForFieldSet = 1244,
+        ApplicationCacheManifestSelectInsecureOrigin = 1245,
+        ApplicationCacheManifestSelectSecureOrigin = 1246,
+        ApplicationCacheAPIInsecureOrigin = 1247,
+        ApplicationCacheAPISecureOrigin = 1248,
 
         // Add new features immediately above this line. Don't change assigned
         // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
index f76fbe11..84db691b 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.cpp
@@ -29,7 +29,10 @@
 #include "core/dom/Document.h"
 #include "core/dom/ExceptionCode.h"
 #include "core/events/EventListener.h"
+#include "core/frame/Deprecation.h"
 #include "core/frame/LocalFrame.h"
+#include "core/frame/OriginsUsingFeatures.h"
+#include "core/frame/UseCounter.h"
 #include "core/loader/DocumentLoader.h"
 #include "core/loader/FrameLoader.h"
 
@@ -65,6 +68,7 @@
 
 unsigned short ApplicationCache::status() const
 {
+    recordAPIUseType();
     ApplicationCacheHost* cacheHost = applicationCacheHost();
     if (!cacheHost)
         return ApplicationCacheHost::UNCACHED;
@@ -73,6 +77,7 @@
 
 void ApplicationCache::update(ExceptionState& exceptionState)
 {
+    recordAPIUseType();
     ApplicationCacheHost* cacheHost = applicationCacheHost();
     if (!cacheHost || !cacheHost->update())
         exceptionState.throwDOMException(InvalidStateError, "there is no application cache to update.");
@@ -80,6 +85,7 @@
 
 void ApplicationCache::swapCache(ExceptionState& exceptionState)
 {
+    recordAPIUseType();
     ApplicationCacheHost* cacheHost = applicationCacheHost();
     if (!cacheHost || !cacheHost->swapCache())
         exceptionState.throwDOMException(InvalidStateError, "there is no newer application cache to swap to.");
@@ -128,4 +134,24 @@
     return EventTypeNames::error;
 }
 
+void ApplicationCache::recordAPIUseType() const
+{
+    if (!m_frame)
+        return;
+
+    Document* document = m_frame->document();
+
+    if (!document)
+        return;
+
+    if (document->isSecureContext()) {
+        UseCounter::count(document, UseCounter::ApplicationCacheAPISecureOrigin);
+        UseCounter::countCrossOriginIframe(*document, UseCounter::ApplicationCacheAPISecureOrigin);
+    } else {
+        Deprecation::countDeprecation(document, UseCounter::ApplicationCacheAPIInsecureOrigin);
+        UseCounter::countCrossOriginIframe(*document, UseCounter::ApplicationCacheAPIInsecureOrigin);
+        OriginsUsingFeatures::countAnyWorld(*document, OriginsUsingFeatures::Feature::ApplicationCacheAPIInsecureOrigin);
+    }
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
index c889831..032800b 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCache.h
@@ -83,6 +83,8 @@
 private:
     explicit ApplicationCache(LocalFrame*);
 
+    void recordAPIUseType() const;
+
     ApplicationCacheHost* applicationCacheHost() const;
 };
 
diff --git a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
index ad70de0..fb2e8a71 100644
--- a/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
+++ b/third_party/WebKit/Source/core/loader/appcache/ApplicationCacheHost.cpp
@@ -33,8 +33,11 @@
 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
 #include "core/events/ApplicationCacheErrorEvent.h"
 #include "core/events/ProgressEvent.h"
+#include "core/frame/Deprecation.h"
 #include "core/frame/LocalFrame.h"
+#include "core/frame/OriginsUsingFeatures.h"
 #include "core/frame/Settings.h"
+#include "core/frame/UseCounter.h"
 #include "core/inspector/InspectorApplicationCacheAgent.h"
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/loader/DocumentLoader.h"
@@ -115,14 +118,25 @@
 
 void ApplicationCacheHost::selectCacheWithManifest(const KURL& manifestURL)
 {
+    DCHECK(m_documentLoader);
+
+    LocalFrame* frame = m_documentLoader->frame();
+    Document* document = frame->document();
+    if (document->isSecureContext()) {
+        UseCounter::count(document, UseCounter::ApplicationCacheManifestSelectSecureOrigin);
+        UseCounter::countCrossOriginIframe(*document, UseCounter::ApplicationCacheManifestSelectSecureOrigin);
+    } else {
+        Deprecation::countDeprecation(document, UseCounter::ApplicationCacheManifestSelectInsecureOrigin);
+        UseCounter::countCrossOriginIframe(*document, UseCounter::ApplicationCacheManifestSelectInsecureOrigin);
+        OriginsUsingFeatures::countAnyWorld(*document, OriginsUsingFeatures::Feature::ApplicationCacheManifestSelectInsecureOrigin);
+    }
     if (m_host && !m_host->selectCacheWithManifest(manifestURL)) {
         // It's a foreign entry, restart the current navigation from the top
         // of the navigation algorithm. The navigation will not result in the
         // same resource being loaded, because "foreign" entries are never picked
         // during navigation.
         // see ApplicationCacheGroup::selectCache()
-        LocalFrame* frame = m_documentLoader->frame();
-        frame->navigate(*frame->document(), frame->document()->url(), true, UserGestureStatus::None);
+        frame->navigate(*document, document->url(), true, UserGestureStatus::None);
     }
 }
 
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index c9c910a..f9997cb 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -67282,6 +67282,12 @@
   <int value="1241" label="MediaStreamOnEnded"/>
   <int value="1242" label="DocumentCreateEventInputEvent"/>
   <int value="1243" label="WebAnimationHyphenatedProperty"/>
+  <int value="1244"
+      label="FormControlsCollectionReturnsRadioNodeListForFieldSet"/>
+  <int value="1245" label="ApplicationCacheManifestSelectInsecureOrigin"/>
+  <int value="1246" label="ApplicationCacheManifestSelectSecureOrigin"/>
+  <int value="1247" label="ApplicationCacheAPIInsecureOrigin"/>
+  <int value="1248" label="ApplicationCacheAPISecureOrigin"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
diff --git a/tools/metrics/rappor/rappor.xml b/tools/metrics/rappor/rappor.xml
index 2d3a2f26..4de1937 100644
--- a/tools/metrics/rappor/rappor.xml
+++ b/tools/metrics/rappor/rappor.xml
@@ -1004,6 +1004,25 @@
   </summary>
 </rappor-metric>
 
+<rappor-metric name="PowerfulFeatureUse.Host.ApplicationCacheAPI.Insecure"
+    type="ETLD_PLUS_ONE">
+  <owner>jww@chromium.org</owner>
+  <summary>
+    The host of the URL that uses a the Application Cache programmatic API from
+    an insecure origin.
+  </summary>
+</rappor-metric>
+
+<rappor-metric
+    name="PowerfulFeatureUse.Host.ApplicationCacheManifestSelect.Insecure"
+    type="ETLD_PLUS_ONE">
+  <owner>jww@chromium.org</owner>
+  <summary>
+    The host of the URL that uses an Application Cache manifest from an insecure
+    origin.
+  </summary>
+</rappor-metric>
+
 <rappor-metric name="PowerfulFeatureUse.Host.DeviceMotion.Insecure"
     type="ETLD_PLUS_ONE">
   <owner>jww@chromium.org</owner>