Implement ReportingObserverOptions and the |buffered| option.

As per this spec:
https://wicg.github.io/reporting/#dictdef-reportingobserveroptions

Bug: 731810
Change-Id: I50840a97c2858fd04f397fd9799156d8e1e873c5
Reviewed-on: https://chromium-review.googlesource.com/1101312
Commit-Queue: Paul Meyer <paulmeyer@chromium.org>
Reviewed-by: Nate Chapin <japhet@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568274}
diff --git a/third_party/WebKit/LayoutTests/reporting-observer/buffering.html b/third_party/WebKit/LayoutTests/reporting-observer/buffering.html
new file mode 100644
index 0000000..0368a10
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/reporting-observer/buffering.html
@@ -0,0 +1,51 @@
+<!doctype html>
+<script src="../resources/testharness.js"></script>
+<script src="../resources/testharnessreport.js"></script>
+<script src="resources/intervention.js"></script>
+
+<div id="target" style="padding: 10px; background-color: blue;">
+  <p>Testing ReportingObserver's |buffered| option.</p>
+</div>
+
+<script>
+async_test(function(test) {
+  var observer1 = new ReportingObserver(function(reports, observer) {
+    test.step(function() {
+      // Both reports should be observed, since the |buffered| option is set to
+      // true.
+      assert_equals(reports.length, 2);
+    });
+
+    test.done();
+  }, { buffered: true });
+
+  // Generate two reports (deprecation and intervention), one before
+  // and one after calling observe().
+  causeIntervention();
+  observer1.observe();
+  window.webkitStorageInfo;
+  observer1.disconnect();
+}, "Buffered reports observed");
+
+
+async_test(function(test) {
+  var observer2 = new ReportingObserver(function(reports, observer) {
+    test.step(function() {
+      // Only the second report should be observed, since the |buffered| option
+      // is set to false by default.
+      assert_equals(reports.length, 1);
+      assert_equals(reports[0].type, "deprecation");
+      assert_equals(reports[0].body.id, "PrefixedWindowURL");
+    });
+
+    test.done();
+  });
+
+  // Generate two reports (deprecation and intervention), one before
+  // and one after calling observe().
+  causeIntervention();
+  observer2.observe();
+  window.webkitURL;  // id = "PrefixedWindowURL"
+  observer2.disconnect();
+}, "Buffered reports not observed");
+</script>
diff --git a/third_party/blink/renderer/core/core_idl_files.gni b/third_party/blink/renderer/core/core_idl_files.gni
index 63601ab..5836b3a 100644
--- a/third_party/blink/renderer/core/core_idl_files.gni
+++ b/third_party/blink/renderer/core/core_idl_files.gni
@@ -611,6 +611,7 @@
                     "fetch/response_init.idl",
                     "fileapi/blob_property_bag.idl",
                     "fileapi/file_property_bag.idl",
+                    "frame/reporting_observer_options.idl",
                     "frame/scroll_into_view_options.idl",
                     "frame/scroll_options.idl",
                     "frame/scroll_to_options.idl",
diff --git a/third_party/blink/renderer/core/frame/deprecation.cc b/third_party/blink/renderer/core/frame/deprecation.cc
index 02913ee..cfc92fa 100644
--- a/third_party/blink/renderer/core/frame/deprecation.cc
+++ b/third_party/blink/renderer/core/frame/deprecation.cc
@@ -771,9 +771,7 @@
   Report* report = new Report("deprecation", document->Url().GetString(), body);
 
   // Send the deprecation report to any ReportingObservers.
-  ReportingContext* reporting_context = ReportingContext::From(document);
-  if (reporting_context->ObserverExists())
-    reporting_context->QueueReport(report);
+  ReportingContext::From(document)->QueueReport(report);
 
   // Send the deprecation report to the Reporting API.
   mojom::blink::ReportingServiceProxyPtr service;
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.idl b/third_party/blink/renderer/core/frame/deprecation_report_body.idl
index a9d36bf..41b6e3d 100644
--- a/third_party/blink/renderer/core/frame/deprecation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/reporting/blob/master/EXPLAINER.md#reportingobserver---observing-reports-from-javascript
+// https://wicg.github.io/reporting/#deprecation-report
 
 [
     NoInterfaceObject,
diff --git a/third_party/blink/renderer/core/frame/intervention.cc b/third_party/blink/renderer/core/frame/intervention.cc
index 8b631d03..9bc1bafc 100644
--- a/third_party/blink/renderer/core/frame/intervention.cc
+++ b/third_party/blink/renderer/core/frame/intervention.cc
@@ -39,9 +39,7 @@
       new Report("intervention", document->Url().GetString(), body);
 
   // Send the intervention report to any ReportingObservers.
-  ReportingContext* reporting_context = ReportingContext::From(document);
-  if (reporting_context->ObserverExists())
-    reporting_context->QueueReport(report);
+  ReportingContext::From(document)->QueueReport(report);
 
   // Send the intervention report to the Reporting API.
   mojom::blink::ReportingServiceProxyPtr service;
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.idl b/third_party/blink/renderer/core/frame/intervention_report_body.idl
index 0a44ecc..52f0b82 100644
--- a/third_party/blink/renderer/core/frame/intervention_report_body.idl
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.idl
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-// https://github.com/WICG/reporting/blob/master/EXPLAINER.md#reportingobserver---observing-reports-from-javascript
+// https://wicg.github.io/reporting/#intervention-report
 
 [
     NoInterfaceObject,
diff --git a/third_party/blink/renderer/core/frame/reporting_context.cc b/third_party/blink/renderer/core/frame/reporting_context.cc
index d41250e..b8c64581 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.cc
+++ b/third_party/blink/renderer/core/frame/reporting_context.cc
@@ -4,8 +4,9 @@
 
 #include "third_party/blink/renderer/core/frame/reporting_context.h"
 
-#include "third_party/blink/public/platform/task_type.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
+#include "third_party/blink/renderer/core/frame/local_frame.h"
 #include "third_party/blink/renderer/core/frame/report.h"
 #include "third_party/blink/renderer/core/frame/reporting_observer.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
@@ -30,45 +31,34 @@
 }
 
 void ReportingContext::QueueReport(Report* report) {
-  if (!ObserverExists())
-    return;
+  report_buffer_.insert(report);
 
-  reports_.push_back(report);
+  // Only the most recent 100 reports will remain buffered.
+  // https://wicg.github.io/reporting/#notify-observers
+  if (report_buffer_.size() > 100)
+    report_buffer_.RemoveFirst();
 
-  // When the first report of a batch is queued, make a task to report the whole
-  // batch (in the queue) to all ReportingObservers.
-  if (reports_.size() == 1) {
-    execution_context_->GetTaskRunner(TaskType::kMiscPlatformAPI)
-        ->PostTask(FROM_HERE, WTF::Bind(&ReportingContext::SendReports,
-                                        WrapWeakPersistent(this)));
-  }
-}
-
-void ReportingContext::SendReports() {
-  // The reports queued to be sent to callbacks are copied (and cleared) before
-  // being sent to observers, since additional reports may be queued as a result
-  // of the callbacks.
-  auto reports_to_send = reports_;
-  reports_.clear();
   for (auto observer : observers_)
-    observer->ReportToCallback(reports_to_send);
+    observer->QueueReport(report);
 }
 
 void ReportingContext::RegisterObserver(ReportingObserver* observer) {
   observers_.insert(observer);
+  if (!observer->Buffered())
+    return;
+
+  observer->ClearBuffered();
+  for (auto report : report_buffer_)
+    observer->QueueReport(report);
 }
 
 void ReportingContext::UnregisterObserver(ReportingObserver* observer) {
   observers_.erase(observer);
 }
 
-bool ReportingContext::ObserverExists() {
-  return observers_.size();
-}
-
 void ReportingContext::Trace(blink::Visitor* visitor) {
   visitor->Trace(observers_);
-  visitor->Trace(reports_);
+  visitor->Trace(report_buffer_);
   visitor->Trace(execution_context_);
   Supplement<ExecutionContext>::Trace(visitor);
 }
diff --git a/third_party/blink/renderer/core/frame/reporting_context.h b/third_party/blink/renderer/core/frame/reporting_context.h
index ed322d7..f75fbdef8 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.h
+++ b/third_party/blink/renderer/core/frame/reporting_context.h
@@ -30,23 +30,17 @@
   // already exist for the given context, one is created.
   static ReportingContext* From(ExecutionContext*);
 
-  // Queues a report to be reported to all observers.
+  // Queues a report in all registered observers.
   void QueueReport(Report*);
 
-  // Sends all queued reports to all observers.
-  void SendReports();
-
   void RegisterObserver(ReportingObserver*);
   void UnregisterObserver(ReportingObserver*);
 
-  // Returns whether there is at least one active ReportingObserver.
-  bool ObserverExists();
-
   void Trace(blink::Visitor*) override;
 
  private:
   HeapListHashSet<Member<ReportingObserver>> observers_;
-  HeapVector<Member<Report>> reports_;
+  HeapListHashSet<Member<Report>> report_buffer_;
   Member<ExecutionContext> execution_context_;
 };
 
diff --git a/third_party/blink/renderer/core/frame/reporting_observer.cc b/third_party/blink/renderer/core/frame/reporting_observer.cc
index 9db714b..1caf300 100644
--- a/third_party/blink/renderer/core/frame/reporting_observer.cc
+++ b/third_party/blink/renderer/core/frame/reporting_observer.cc
@@ -4,26 +4,56 @@
 
 #include "third_party/blink/renderer/core/frame/reporting_observer.h"
 
+#include "third_party/blink/public/platform/task_type.h"
 #include "third_party/blink/renderer/core/execution_context/execution_context.h"
 #include "third_party/blink/renderer/core/frame/report.h"
 #include "third_party/blink/renderer/core/frame/reporting_context.h"
 #include "third_party/blink/renderer/platform/bindings/script_state.h"
+#include "third_party/blink/renderer/platform/web_task_runner.h"
 
 namespace blink {
 
 ReportingObserver* ReportingObserver::Create(
     ExecutionContext* execution_context,
-    V8ReportingObserverCallback* callback) {
-  return new ReportingObserver(execution_context, callback);
+    V8ReportingObserverCallback* callback,
+    ReportingObserverOptions options) {
+  return new ReportingObserver(execution_context, callback, options);
 }
 
 ReportingObserver::ReportingObserver(ExecutionContext* execution_context,
-                                     V8ReportingObserverCallback* callback)
-    : execution_context_(execution_context), callback_(callback) {}
+                                     V8ReportingObserverCallback* callback,
+                                     ReportingObserverOptions options)
+    : execution_context_(execution_context),
+      callback_(callback),
+      options_(options) {}
 
-void ReportingObserver::ReportToCallback(
-    const HeapVector<Member<Report>>& reports) {
-  callback_->InvokeAndReportException(this, reports, this);
+void ReportingObserver::ReportToCallback() {
+  // The reports queued to be sent to callbacks are copied (and cleared) before
+  // being sent, since additional reports may be queued as a result of the
+  // callbacks.
+  auto reports_to_send = report_queue_;
+  report_queue_.clear();
+  callback_->InvokeAndReportException(this, reports_to_send, this);
+}
+
+void ReportingObserver::QueueReport(Report* report) {
+  report_queue_.push_back(report);
+
+  // When the first report of a batch is queued, make a task to report the whole
+  // batch.
+  if (report_queue_.size() == 1) {
+    execution_context_->GetTaskRunner(TaskType::kMiscPlatformAPI)
+        ->PostTask(FROM_HERE, WTF::Bind(&ReportingObserver::ReportToCallback,
+                                        WrapWeakPersistent(this)));
+  }
+}
+
+bool ReportingObserver::Buffered() {
+  return options_.buffered();
+}
+
+void ReportingObserver::ClearBuffered() {
+  return options_.setBuffered(false);
 }
 
 void ReportingObserver::observe() {
@@ -37,6 +67,7 @@
 void ReportingObserver::Trace(blink::Visitor* visitor) {
   visitor->Trace(execution_context_);
   visitor->Trace(callback_);
+  visitor->Trace(report_queue_);
   ScriptWrappable::Trace(visitor);
 }
 
diff --git a/third_party/blink/renderer/core/frame/reporting_observer.h b/third_party/blink/renderer/core/frame/reporting_observer.h
index 5c7350e4..4c282d62 100644
--- a/third_party/blink/renderer/core/frame/reporting_observer.h
+++ b/third_party/blink/renderer/core/frame/reporting_observer.h
@@ -7,6 +7,8 @@
 
 #include "third_party/blink/renderer/bindings/core/v8/v8_reporting_observer_callback.h"
 #include "third_party/blink/renderer/core/core_export.h"
+#include "third_party/blink/renderer/core/frame/report.h"
+#include "third_party/blink/renderer/core/frame/reporting_observer_options.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 #include "third_party/blink/renderer/platform/wtf/vector.h"
 
@@ -20,10 +22,21 @@
 
  public:
   static ReportingObserver* Create(ExecutionContext*,
-                                   V8ReportingObserverCallback*);
+                                   V8ReportingObserverCallback*,
+                                   ReportingObserverOptions);
 
-  // Call the callback with reports.
-  void ReportToCallback(const HeapVector<Member<Report>>& reports);
+  // Call the callback with all reports in |report_queue_|.
+  void ReportToCallback();
+
+  // Queues a report to be reported via callback soon (possibly in a batch).
+  void QueueReport(Report* report);
+
+  // Returns the state of the |buffered| option.
+  bool Buffered();
+
+  // Sets the |buffered| option to false. This should be called after queueing
+  // all buffered reports, so that they are not reported multiple times.
+  void ClearBuffered();
 
   void observe();
   void disconnect();
@@ -31,10 +44,14 @@
   void Trace(blink::Visitor*) override;
 
  private:
-  explicit ReportingObserver(ExecutionContext*, V8ReportingObserverCallback*);
+  explicit ReportingObserver(ExecutionContext*,
+                             V8ReportingObserverCallback*,
+                             ReportingObserverOptions);
 
   Member<ExecutionContext> execution_context_;
   Member<V8ReportingObserverCallback> callback_;
+  ReportingObserverOptions options_;
+  HeapVector<Member<Report>> report_queue_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/reporting_observer.idl b/third_party/blink/renderer/core/frame/reporting_observer.idl
index f3cbb925..ef1a601c 100644
--- a/third_party/blink/renderer/core/frame/reporting_observer.idl
+++ b/third_party/blink/renderer/core/frame/reporting_observer.idl
@@ -7,7 +7,7 @@
 callback ReportingObserverCallback = void (sequence<Report> reports, ReportingObserver observer);
 
 [
-    Constructor(ReportingObserverCallback callback),
+    Constructor(ReportingObserverCallback callback, optional ReportingObserverOptions options),
     ConstructorCallWith=ExecutionContext,
     RuntimeEnabled=ReportingObserver
 ] interface ReportingObserver {
diff --git a/third_party/blink/renderer/core/frame/reporting_observer_options.idl b/third_party/blink/renderer/core/frame/reporting_observer_options.idl
new file mode 100644
index 0000000..daa436e
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/reporting_observer_options.idl
@@ -0,0 +1,9 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// https://wicg.github.io/reporting/#dictdef-reportingobserveroptions
+
+dictionary ReportingObserverOptions {
+  boolean buffered = false;
+};