Reporting: Check upload permissions asynchronously

This changes the signature of the CanSendReport method so that it works
asynchronously.  We need this to able to check the background sync
permission, since that can only be checked from the UI thread; we need
the async logic to pass control between the IO and UI threads in the
right way.

To reduce the number of async calls, the method has also been updated to
check several origins all at once, instead of having a single method
call for each origin.  You pass in a set of origins that you'd like to
upload reports for, and the method passes back the set of origins that
you're allowed to.

Bug: 704259
Change-Id: I431b2fe1c1eb4e65cea91a86fd213e3204b852a1
Reviewed-on: https://chromium-review.googlesource.com/937566
Reviewed-by: Julia Tuttle <[email protected]>
Commit-Queue: Douglas Creager <[email protected]>
Cr-Commit-Position: refs/heads/master@{#539546}
diff --git a/chrome/browser/net/chrome_network_delegate.cc b/chrome/browser/net/chrome_network_delegate.cc
index 623f39b..7500b1f 100644
--- a/chrome/browser/net/chrome_network_delegate.cc
+++ b/chrome/browser/net/chrome_network_delegate.cc
@@ -532,13 +532,24 @@
                                                  origin.GetURL());
 }
 
-bool ChromeNetworkDelegate::OnCanSendReportingReport(
-    const url::Origin& origin) const {
-  if (!cookie_settings_)
-    return true;
+void ChromeNetworkDelegate::OnCanSendReportingReports(
+    std::set<url::Origin> origins,
+    base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
+  if (!cookie_settings_) {
+    std::move(result_callback).Run(std::move(origins));
+    return;
+  }
 
-  return cookie_settings_->IsCookieAccessAllowed(origin.GetURL(),
-                                                 origin.GetURL());
+  for (auto it = origins.begin(); it != origins.end();) {
+    const auto& origin = *it;
+    if (!cookie_settings_->IsCookieAccessAllowed(origin.GetURL(),
+                                                 origin.GetURL())) {
+      origins.erase(it++);
+    } else {
+      ++it;
+    }
+  }
+  std::move(result_callback).Run(std::move(origins));
 }
 
 bool ChromeNetworkDelegate::OnCanSetReportingClient(
diff --git a/chrome/browser/net/chrome_network_delegate.h b/chrome/browser/net/chrome_network_delegate.h
index 964b589..18c62a1 100644
--- a/chrome/browser/net/chrome_network_delegate.h
+++ b/chrome/browser/net/chrome_network_delegate.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <set>
 #include <string>
 
 #include "base/compiler_specific.h"
@@ -189,7 +190,9 @@
       const GURL& target_url,
       const GURL& referrer_url) const override;
   bool OnCanQueueReportingReport(const url::Origin& origin) const override;
-  bool OnCanSendReportingReport(const url::Origin& origin) const override;
+  void OnCanSendReportingReports(std::set<url::Origin> origins,
+                                 base::OnceCallback<void(std::set<url::Origin>)>
+                                     result_callback) const override;
   bool OnCanSetReportingClient(const url::Origin& origin,
                                const GURL& endpoint) const override;
   bool OnCanUseReportingClient(const url::Origin& origin,
diff --git a/net/base/layered_network_delegate.cc b/net/base/layered_network_delegate.cc
index 46bd4b2..e771fa9 100644
--- a/net/base/layered_network_delegate.cc
+++ b/net/base/layered_network_delegate.cc
@@ -262,14 +262,16 @@
 void LayeredNetworkDelegate::OnCanQueueReportingReportInternal(
     const url::Origin& origin) const {}
 
-bool LayeredNetworkDelegate::OnCanSendReportingReport(
-    const url::Origin& origin) const {
-  OnCanSendReportingReportInternal(origin);
-  return nested_network_delegate_->CanSendReportingReport(origin);
+void LayeredNetworkDelegate::OnCanSendReportingReports(
+    std::set<url::Origin> origins,
+    base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
+  OnCanSendReportingReportsInternal(origins);
+  nested_network_delegate_->CanSendReportingReports(std::move(origins),
+                                                    std::move(result_callback));
 }
 
-void LayeredNetworkDelegate::OnCanSendReportingReportInternal(
-    const url::Origin& origin) const {}
+void LayeredNetworkDelegate::OnCanSendReportingReportsInternal(
+    const std::set<url::Origin>& origins) const {}
 
 bool LayeredNetworkDelegate::OnCanSetReportingClient(
     const url::Origin& origin,
diff --git a/net/base/layered_network_delegate.h b/net/base/layered_network_delegate.h
index f9deec7..56cac40 100644
--- a/net/base/layered_network_delegate.h
+++ b/net/base/layered_network_delegate.h
@@ -8,6 +8,7 @@
 #include <stdint.h>
 
 #include <memory>
+#include <set>
 
 #include "base/strings/string16.h"
 #include "net/base/completion_callback.h"
@@ -90,7 +91,9 @@
 
   bool OnCanQueueReportingReport(const url::Origin& origin) const final;
 
-  bool OnCanSendReportingReport(const url::Origin& origin) const final;
+  void OnCanSendReportingReports(std::set<url::Origin> origins,
+                                 base::OnceCallback<void(std::set<url::Origin>)>
+                                     result_callback) const final;
 
   bool OnCanSetReportingClient(const url::Origin& origin,
                                const GURL& endpoint) const final;
@@ -173,8 +176,8 @@
   virtual void OnCanQueueReportingReportInternal(
       const url::Origin& origin) const;
 
-  virtual void OnCanSendReportingReportInternal(
-      const url::Origin& origin) const;
+  virtual void OnCanSendReportingReportsInternal(
+      const std::set<url::Origin>& origins) const;
 
   virtual void OnCanSetReportingClientInternal(const url::Origin& origin,
                                                const GURL& endpoint) const;
diff --git a/net/base/layered_network_delegate_unittest.cc b/net/base/layered_network_delegate_unittest.cc
index 473c379f..5f5383a 100644
--- a/net/base/layered_network_delegate_unittest.cc
+++ b/net/base/layered_network_delegate_unittest.cc
@@ -343,8 +343,8 @@
     EXPECT_EQ(1, (*counters_)["on_can_queue_reporting_report_count"]);
   }
 
-  void OnCanSendReportingReportInternal(
-      const url::Origin& origin) const override {
+  void OnCanSendReportingReportsInternal(
+      const std::set<url::Origin>& origins) const override {
     ++(*counters_)["on_can_send_reporting_report_count"];
     EXPECT_EQ(1, (*counters_)["on_can_send_reporting_report_count"]);
   }
diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc
index c16948c..20271bcb 100644
--- a/net/base/network_delegate.cc
+++ b/net/base/network_delegate.cc
@@ -188,9 +188,11 @@
   return OnCanQueueReportingReport(origin);
 }
 
-bool NetworkDelegate::CanSendReportingReport(const url::Origin& origin) const {
+void NetworkDelegate::CanSendReportingReports(
+    std::set<url::Origin> origins,
+    base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
-  return OnCanSendReportingReport(origin);
+  OnCanSendReportingReports(std::move(origins), std::move(result_callback));
 }
 
 bool NetworkDelegate::CanSetReportingClient(const url::Origin& origin,
diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h
index 5742cb7..e3b6502f 100644
--- a/net/base/network_delegate.h
+++ b/net/base/network_delegate.h
@@ -7,6 +7,7 @@
 
 #include <stdint.h>
 
+#include <set>
 #include <string>
 
 #include "base/callback.h"
@@ -118,7 +119,9 @@
       const GURL& referrer_url) const;
 
   bool CanQueueReportingReport(const url::Origin& origin) const;
-  bool CanSendReportingReport(const url::Origin& origin) const;
+  void CanSendReportingReports(
+      std::set<url::Origin> origins,
+      base::OnceCallback<void(std::set<url::Origin>)> result_callback) const;
   bool CanSetReportingClient(const url::Origin& origin,
                              const GURL& endpoint) const;
   bool CanUseReportingClient(const url::Origin& origin,
@@ -310,7 +313,10 @@
 
   virtual bool OnCanQueueReportingReport(const url::Origin& origin) const = 0;
 
-  virtual bool OnCanSendReportingReport(const url::Origin& origin) const = 0;
+  virtual void OnCanSendReportingReports(
+      std::set<url::Origin> origins,
+      base::OnceCallback<void(std::set<url::Origin>)> result_callback)
+      const = 0;
 
   virtual bool OnCanSetReportingClient(const url::Origin& origin,
                                        const GURL& endpoint) const = 0;
diff --git a/net/base/network_delegate_impl.cc b/net/base/network_delegate_impl.cc
index 66b1247..46e7eb64 100644
--- a/net/base/network_delegate_impl.cc
+++ b/net/base/network_delegate_impl.cc
@@ -116,9 +116,10 @@
   return true;
 }
 
-bool NetworkDelegateImpl::OnCanSendReportingReport(
-    const url::Origin& origin) const {
-  return true;
+void NetworkDelegateImpl::OnCanSendReportingReports(
+    std::set<url::Origin> origins,
+    base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
+  std::move(result_callback).Run(std::move(origins));
 }
 
 bool NetworkDelegateImpl::OnCanSetReportingClient(const url::Origin& origin,
diff --git a/net/base/network_delegate_impl.h b/net/base/network_delegate_impl.h
index e1a853b..8a991a1 100644
--- a/net/base/network_delegate_impl.h
+++ b/net/base/network_delegate_impl.h
@@ -7,6 +7,8 @@
 
 #include <stdint.h>
 
+#include <set>
+
 #include "base/strings/string16.h"
 #include "net/base/completion_callback.h"
 #include "net/base/net_export.h"
@@ -104,7 +106,9 @@
 
   bool OnCanQueueReportingReport(const url::Origin& origin) const override;
 
-  bool OnCanSendReportingReport(const url::Origin& origin) const override;
+  void OnCanSendReportingReports(std::set<url::Origin> origins,
+                                 base::OnceCallback<void(std::set<url::Origin>)>
+                                     result_callback) const override;
 
   bool OnCanSetReportingClient(const url::Origin& origin,
                                const GURL& endpoint) const override;
diff --git a/net/reporting/reporting_delegate.cc b/net/reporting/reporting_delegate.cc
index 8788137a..e25531a 100644
--- a/net/reporting/reporting_delegate.cc
+++ b/net/reporting/reporting_delegate.cc
@@ -26,9 +26,16 @@
            network_delegate()->CanQueueReportingReport(origin);
   }
 
-  bool CanSendReport(const url::Origin& origin) const override {
-    return network_delegate() &&
-           network_delegate()->CanSendReportingReport(origin);
+  void CanSendReports(std::set<url::Origin> origins,
+                      base::OnceCallback<void(std::set<url::Origin>)>
+                          result_callback) const override {
+    if (!network_delegate()) {
+      origins.clear();
+      std::move(result_callback).Run(std::move(origins));
+      return;
+    }
+    network_delegate()->CanSendReportingReports(std::move(origins),
+                                                std::move(result_callback));
   }
 
   bool CanSetClient(const url::Origin& origin,
diff --git a/net/reporting/reporting_delegate.h b/net/reporting/reporting_delegate.h
index ba8d7a9..23808fa 100644
--- a/net/reporting/reporting_delegate.h
+++ b/net/reporting/reporting_delegate.h
@@ -6,6 +6,7 @@
 #define NET_REPORTING_REPORTING_DELEGATE_H_
 
 #include <memory>
+#include <set>
 
 #include "base/callback.h"
 #include "base/macros.h"
@@ -29,9 +30,11 @@
   // Checks whether |origin| is allowed to queue reports for future delivery.
   virtual bool CanQueueReport(const url::Origin& origin) const = 0;
 
-  // Checks whether |origin| is allowed to receive reports, either in real time
-  // or that were queued earlier.
-  virtual bool CanSendReport(const url::Origin& origin) const = 0;
+  // Checks whether |origins| are allowed to receive reports, either in real
+  // time or that were queued earlier, removing any that aren't.
+  virtual void CanSendReports(std::set<url::Origin> origins,
+                              base::OnceCallback<void(std::set<url::Origin>)>
+                                  result_callback) const = 0;
 
   // Checks whether |origin| can set |endpoint| to be used for future report
   // deliveries.
diff --git a/net/reporting/reporting_delivery_agent.cc b/net/reporting/reporting_delivery_agent.cc
index 8786e30..e370cdd 100644
--- a/net/reporting/reporting_delivery_agent.cc
+++ b/net/reporting/reporting_delivery_agent.cc
@@ -112,17 +112,40 @@
     std::vector<const ReportingReport*> reports;
     cache()->GetReports(&reports);
 
+    // First determine which origins we're allowed to upload reports about.
+    std::set<url::Origin> origins;
+    for (const ReportingReport* report : reports) {
+      origins.insert(url::Origin::Create(report->url));
+    }
+    delegate()->CanSendReports(
+        std::move(origins),
+        base::BindOnce(&ReportingDeliveryAgentImpl::OnSendPermissionsChecked,
+                       weak_factory_.GetWeakPtr(), std::move(reports)));
+  }
+
+  void OnSendPermissionsChecked(std::vector<const ReportingReport*> reports,
+                                std::set<url::Origin> allowed_origins) {
+    std::vector<const ReportingReport*> disallowed_reports;
+
     // Sort reports into (origin, group) buckets.
     std::map<OriginGroup, std::vector<const ReportingReport*>>
         origin_group_reports;
     for (const ReportingReport* report : reports) {
       url::Origin origin = url::Origin::Create(report->url);
-      if (!delegate()->CanSendReport(origin))
+      if (allowed_origins.find(origin) == allowed_origins.end()) {
+        disallowed_reports.push_back(report);
         continue;
-      OriginGroup origin_group(url::Origin::Create(report->url), report->group);
+      }
+      OriginGroup origin_group(origin, report->group);
       origin_group_reports[origin_group].push_back(report);
     }
 
+    // Remove from the cache any reports that we're not allowed to upload.
+    cache()->RemoveReports(
+        disallowed_reports,
+        ReportingReport::Outcome::ERASED_NO_BACKGROUND_SYNC_PERMISSION);
+    disallowed_reports.clear();
+
     // Find endpoint for each (origin, group) bucket and sort reports into
     // endpoint buckets. Don't allow concurrent deliveries to the same (origin,
     // group) bucket.
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
index c4e233a9..c9484ce 100644
--- a/net/reporting/reporting_delivery_agent_unittest.cc
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -126,6 +126,35 @@
   EXPECT_TRUE(pending_uploads().empty());
 }
 
+TEST_F(ReportingDeliveryAgentTest, DisallowedUpload) {
+  // This mimics the check that is controlled by the BACKGROUND_SYNC permission
+  // in a real browser profile.
+  context()->test_delegate()->set_disallow_report_uploads(true);
+
+  static const int kAgeMillis = 12345;
+
+  base::DictionaryValue body;
+  body.SetString("key", "value");
+
+  SetClient(kOrigin_, kEndpoint_, kGroup_);
+  cache()->AddReport(kUrl_, kGroup_, kType_, body.CreateDeepCopy(),
+                     tick_clock()->NowTicks(), 0);
+
+  tick_clock()->Advance(base::TimeDelta::FromMilliseconds(kAgeMillis));
+
+  EXPECT_TRUE(delivery_timer()->IsRunning());
+  delivery_timer()->Fire();
+
+  // We should not try to upload the report, since we weren't given permission
+  // for this origin.
+  EXPECT_TRUE(pending_uploads().empty());
+
+  // Disallowed reports should have been removed from the cache.
+  std::vector<const ReportingReport*> reports;
+  cache()->GetReports(&reports);
+  EXPECT_TRUE(reports.empty());
+}
+
 TEST_F(ReportingDeliveryAgentTest, RemoveEndpointUpload) {
   static const url::Origin kDifferentOrigin =
       url::Origin::Create(GURL("https://origin2/"));
diff --git a/net/reporting/reporting_report.h b/net/reporting/reporting_report.h
index e7fba5d..1621ab0 100644
--- a/net/reporting/reporting_report.h
+++ b/net/reporting/reporting_report.h
@@ -33,6 +33,7 @@
     ERASED_BROWSING_DATA_REMOVED = 7,
     ERASED_REPORTING_SHUT_DOWN = 8,
     DELIVERED = 9,
+    ERASED_NO_BACKGROUND_SYNC_PERMISSION = 10,
 
     MAX
   };
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index 4dfc21d6..a718f5e5 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -118,8 +118,12 @@
   return true;
 }
 
-bool TestReportingDelegate::CanSendReport(const url::Origin& origin) const {
-  return true;
+void TestReportingDelegate::CanSendReports(
+    std::set<url::Origin> origins,
+    base::OnceCallback<void(std::set<url::Origin>)> result_callback) const {
+  if (disallow_report_uploads_)
+    origins.clear();
+  std::move(result_callback).Run(std::move(origins));
 }
 
 bool TestReportingDelegate::CanSetClient(const url::Origin& origin,
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index fe56e3aa..b9ae0af 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -6,6 +6,7 @@
 #define NET_REPORTING_REPORTING_TEST_UTIL_H_
 
 #include <memory>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -89,9 +90,15 @@
 
   ~TestReportingDelegate() override;
 
+  void set_disallow_report_uploads(bool disallow_report_uploads) {
+    disallow_report_uploads_ = disallow_report_uploads;
+  }
+
   bool CanQueueReport(const url::Origin& origin) const override;
 
-  bool CanSendReport(const url::Origin& origin) const override;
+  void CanSendReports(std::set<url::Origin> origins,
+                      base::OnceCallback<void(std::set<url::Origin>)>
+                          result_callback) const override;
 
   bool CanSetClient(const url::Origin& origin,
                     const GURL& endpoint) const override;
@@ -104,6 +111,8 @@
                  const JsonFailureCallback& failure_callback) const override;
 
  private:
+  bool disallow_report_uploads_ = false;
+
   DISALLOW_COPY_AND_ASSIGN(TestReportingDelegate);
 };
 
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 017c883..920f4e4 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -37775,6 +37775,7 @@
   <int value="7" label="Erased: browsing data removed"/>
   <int value="8" label="Erased: Reporting shut down"/>
   <int value="9" label="Delivered"/>
+  <int value="10" label="Discarded: Background sync not allowed"/>
 </enum>
 
 <enum name="ReportProcessingResult">