Adding toJSON to report and report_body
- declare BuildJSONValue in report_body as virtual
- make all sub-class implement override implementation
add toJSON to report_body, making it serializable
- Includes 4 different reports
- and parent class LocationReportBody
Bug: 917945
Change-Id: I2ef281aad171ec6a477ae562fab3257847ee4f41
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1895957
Reviewed-by: David Bokan <bokan@chromium.org>
Reviewed-by: Jason Chase <chasej@chromium.org>
Commit-Queue: Rodney Ding <rodneyding@google.com>
Cr-Commit-Position: refs/heads/master@{#714244}
diff --git a/third_party/blink/renderer/core/BUILD.gn b/third_party/blink/renderer/core/BUILD.gn
index 4d1144d..aa917a98 100644
--- a/third_party/blink/renderer/core/BUILD.gn
+++ b/third_party/blink/renderer/core/BUILD.gn
@@ -1205,6 +1205,7 @@
     "frame/csp/csp_source_test.cc",
     "frame/csp/media_list_directive_test.cc",
     "frame/csp/source_list_directive_test.cc",
+    "frame/deprecation_report_body_test.cc",
     "frame/document_loading_rendering_test.cc",
     "frame/dom_timer_test.cc",
     "frame/find_in_page_test.cc",
diff --git a/third_party/blink/renderer/core/frame/BUILD.gn b/third_party/blink/renderer/core/frame/BUILD.gn
index 381effd..109f359 100644
--- a/third_party/blink/renderer/core/frame/BUILD.gn
+++ b/third_party/blink/renderer/core/frame/BUILD.gn
@@ -19,6 +19,7 @@
     "csp/csp_directive_list.h",
     "csp/csp_source.cc",
     "csp/csp_source.h",
+    "csp/csp_violation_report_body.cc",
     "csp/csp_violation_report_body.h",
     "csp/execution_context_csp_delegate.cc",
     "csp/execution_context_csp_delegate.h",
@@ -36,6 +37,7 @@
     "deprecated_schedule_style_recalc_during_layout.h",
     "deprecation.cc",
     "deprecation.h",
+    "deprecation_report_body.cc",
     "deprecation_report_body.h",
     "device_single_window_event_controller.cc",
     "device_single_window_event_controller.h",
@@ -54,6 +56,7 @@
     "event_handler_registry.cc",
     "event_handler_registry.h",
     "external.h",
+    "feature_policy_violation_report_body.cc",
     "feature_policy_violation_report_body.h",
     "find_in_page.cc",
     "find_in_page.h",
@@ -83,6 +86,7 @@
     "hosts_using_features.h",
     "intervention.cc",
     "intervention.h",
+    "intervention_report_body.cc",
     "intervention_report_body.h",
     "layout_subtree_root_list.cc",
     "layout_subtree_root_list.h",
@@ -97,6 +101,7 @@
     "local_frame_view.h",
     "location.cc",
     "location.h",
+    "location_report_body.cc",
     "location_report_body.h",
     "navigation_rate_limiter.cc",
     "navigation_rate_limiter.h",
@@ -146,6 +151,7 @@
     "remote_frame_view.h",
     "report.cc",
     "report.h",
+    "report_body.cc",
     "report_body.h",
     "reporting_context.cc",
     "reporting_context.h",
@@ -171,6 +177,7 @@
     "settings_delegate.h",
     "smart_clip.cc",
     "smart_clip.h",
+    "test_report_body.cc",
     "test_report_body.h",
     "use_counter_helper.cc",
     "use_counter_helper.h",
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc
new file mode 100644
index 0000000..a552bc3
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.cc
@@ -0,0 +1,21 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h"
+
+namespace blink {
+
+void CSPViolationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("documentURL", documentURL());
+  builder.AddStringOrNull("referrer", referrer());
+  builder.AddStringOrNull("blockedURL", blockedURL());
+  builder.AddString("effectiveDirective", effectiveDirective());
+  builder.AddString("originalPolicy", originalPolicy());
+  builder.AddStringOrNull("sample", sample());
+  builder.AddString("disposition", disposition());
+  builder.AddNumber("statusCode", statusCode());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
index 8573ec36..2e57116 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_CSP_CSP_VIOLATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/events/security_policy_violation_event_init.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
@@ -45,6 +46,8 @@
   String disposition() const { return disposition_; }
   uint16_t statusCode() const { return status_code_; }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String document_url_;
   const String referrer_;
diff --git a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
index 0ab4e1d..d65ebecf 100644
--- a/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/csp/csp_violation_report_body.idl
@@ -18,4 +18,5 @@
   readonly attribute unsigned short statusCode;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.cc b/third_party/blink/renderer/core/frame/deprecation_report_body.cc
new file mode 100644
index 0000000..353dcc60
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.cc
@@ -0,0 +1,38 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/deprecation_report_body.h"
+#include "third_party/blink/renderer/platform/text/date_components.h"
+#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
+
+namespace blink {
+
+void DeprecationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("id", id());
+  builder.AddString("message", message());
+
+  bool is_null = false;
+  double anticipated_removal_value = anticipatedRemoval(is_null);
+  if (is_null) {
+    builder.AddNull("anticipatedRemoval");
+  } else {
+    DateComponents anticipated_removal_date;
+    bool is_valid =
+        anticipated_removal_date.SetMillisecondsSinceEpochForDateTimeLocal(
+            anticipated_removal_value);
+    if (!is_valid) {
+      builder.AddNull("anticipatedRemoval");
+    } else {
+      // Adding extra 'Z' here to ensure that the string gives the same result
+      // as JSON.stringify(anticipatedRemoval) in javascript. Note here
+      // anticipatedRemoval will become a Date object in javascript.
+      String iso8601_date =
+          anticipated_removal_date.ToString(DateComponents::kMillisecond) + "Z";
+      builder.AddString("anticipatedRemoval", iso8601_date);
+    }
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body.h b/third_party/blink/renderer/core/frame/deprecation_report_body.h
index 2029cd5..7d238996 100644
--- a/third_party/blink/renderer/core/frame/deprecation_report_body.h
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_DEPRECATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -28,6 +29,8 @@
     return anticipatedRemoval_;
   }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String id_;
   const String message_;
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 5b76ce4..7c9916b4 100644
--- a/third_party/blink/renderer/core/frame/deprecation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body.idl
@@ -13,4 +13,5 @@
   readonly attribute DOMString? sourceFile;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/deprecation_report_body_test.cc b/third_party/blink/renderer/core/frame/deprecation_report_body_test.cc
new file mode 100644
index 0000000..0f2d5e9
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/deprecation_report_body_test.cc
@@ -0,0 +1,59 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/deprecation_report_body.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h"
+
+namespace blink {
+
+namespace {
+
+TEST(DeprecationReportBodyJSONTest, noAnticipatedRemoval) {
+  DeprecationReportBody body("test_id", 0, "test_message");
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  V8ObjectBuilder builder(script_state);
+  body.BuildJSONValue(builder);
+  ScriptValue json_object = builder.GetScriptValue();
+  EXPECT_TRUE(json_object.IsObject());
+
+  String json_string = ToBlinkString<String>(
+      v8::JSON::Stringify(scope.GetContext(),
+                          json_object.V8Value().As<v8::Object>())
+          .ToLocalChecked(),
+      kDoNotExternalize);
+
+  String expected =
+      "{\"sourceFile\":null,\"lineNumber\":null,\"columnNumber\":null,\"id\":"
+      "\"test_id\",\"message\":\"test_message\",\"anticipatedRemoval\":null}";
+  EXPECT_EQ(expected, json_string);
+}
+
+TEST(DeprecationReportBodyJSONTest, actualAnticipatedRemoval) {
+  DeprecationReportBody body("test_id", 1575950400000, "test_message");
+  V8TestingScope scope;
+  ScriptState* script_state = scope.GetScriptState();
+  V8ObjectBuilder builder(script_state);
+  body.BuildJSONValue(builder);
+  ScriptValue json_object = builder.GetScriptValue();
+  EXPECT_TRUE(json_object.IsObject());
+
+  String json_string = ToBlinkString<String>(
+      v8::JSON::Stringify(scope.GetContext(),
+                          json_object.V8Value().As<v8::Object>())
+          .ToLocalChecked(),
+      kDoNotExternalize);
+
+  String expected =
+      "{\"sourceFile\":null,\"lineNumber\":null,\"columnNumber\":null,\"id\":"
+      "\"test_id\",\"message\":\"test_message\",\"anticipatedRemoval\":\"2019-"
+      "12-10T04:00:00.000Z\"}";
+  EXPECT_EQ(expected, json_string);
+}
+
+}  // namespace
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc
new file mode 100644
index 0000000..8edb646
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.cc
@@ -0,0 +1,17 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h"
+
+namespace blink {
+
+void FeaturePolicyViolationReportBody::BuildJSONValue(
+    V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("featureId", featureId());
+  builder.AddString("disposition", disposition());
+  builder.AddStringOrNull("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
index 430da4f6d..85251a46 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_FEATURE_POLICY_VIOLATION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -33,6 +34,7 @@
   String featureId() const { return feature_id_; }
   String disposition() const { return disposition_; }
   String message() const { return message_; }
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
 
   ~FeaturePolicyViolationReportBody() override = default;
 
diff --git a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
index b5184ea..dca179c 100644
--- a/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
+++ b/third_party/blink/renderer/core/frame/feature_policy_violation_report_body.idl
@@ -13,4 +13,5 @@
   readonly attribute unsigned long? columnNumber;
   readonly attribute DOMString disposition;
   readonly attribute DOMString? message;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.cc b/third_party/blink/renderer/core/frame/intervention_report_body.cc
new file mode 100644
index 0000000..6ad9854
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/intervention_report_body.h"
+
+namespace blink {
+
+void InterventionReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  LocationReportBody::BuildJSONValue(builder);
+  builder.AddString("id", id());
+  builder.AddString("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/intervention_report_body.h b/third_party/blink/renderer/core/frame/intervention_report_body.h
index 873a319..3b7fdfb 100644
--- a/third_party/blink/renderer/core/frame/intervention_report_body.h
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.h
@@ -6,6 +6,7 @@
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_INTERVENTION_REPORT_BODY_H_
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/location_report_body.h"
 
 namespace blink {
@@ -21,6 +22,7 @@
 
   String id() const { return id_; }
   String message() const { return message_; }
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
 
  private:
   const String id_;
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 156fbc2..1626e2a 100644
--- a/third_party/blink/renderer/core/frame/intervention_report_body.idl
+++ b/third_party/blink/renderer/core/frame/intervention_report_body.idl
@@ -12,4 +12,5 @@
   readonly attribute DOMString? sourceFile;
   readonly attribute unsigned long? lineNumber;
   readonly attribute unsigned long? columnNumber;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/location_report_body.cc b/third_party/blink/renderer/core/frame/location_report_body.cc
new file mode 100644
index 0000000..6525fb03
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/location_report_body.cc
@@ -0,0 +1,27 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/location_report_body.h"
+
+namespace blink {
+
+void LocationReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  builder.AddStringOrNull("sourceFile", sourceFile());
+  bool is_null = false;
+  uint32_t line_number = lineNumber(is_null);
+  if (is_null) {
+    builder.AddNull("lineNumber");
+  } else {
+    builder.AddNumber("lineNumber", line_number);
+  }
+  is_null = true;
+  uint32_t column_number = columnNumber(is_null);
+  if (is_null) {
+    builder.AddNull("columnNumber");
+  } else {
+    builder.AddNumber("columnNumber", column_number);
+  }
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/location_report_body.h b/third_party/blink/renderer/core/frame/location_report_body.h
index 5a87d761..208ea7b 100644
--- a/third_party/blink/renderer/core/frame/location_report_body.h
+++ b/third_party/blink/renderer/core/frame/location_report_body.h
@@ -9,11 +9,12 @@
 #include <utility>
 
 #include "third_party/blink/renderer/bindings/core/v8/source_location.h"
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/report_body.h"
 
 namespace blink {
 
-class LocationReportBody : public ReportBody {
+class CORE_EXPORT LocationReportBody : public ReportBody {
  public:
   explicit LocationReportBody(std::unique_ptr<SourceLocation> location)
       : source_file_(location->Url()),
@@ -47,6 +48,8 @@
     return column_number_.value_or(0);
   }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  protected:
   const String source_file_;
   base::Optional<uint32_t> line_number_;
diff --git a/third_party/blink/renderer/core/frame/report.cc b/third_party/blink/renderer/core/frame/report.cc
index 38b7c5e0..46d331d 100644
--- a/third_party/blink/renderer/core/frame/report.cc
+++ b/third_party/blink/renderer/core/frame/report.cc
@@ -11,4 +11,14 @@
 constexpr const char ReportType::kIntervention[];
 constexpr const char ReportType::kCSPViolation[];
 
+ScriptValue Report::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder builder(script_state);
+  builder.AddString("type", type());
+  builder.AddString("url", url());
+  V8ObjectBuilder body_builder(script_state);
+  body()->BuildJSONValue(body_builder);
+  builder.Add("body", body_builder);
+  return builder.GetScriptValue();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report.h b/third_party/blink/renderer/core/frame/report.h
index 3aba915a..3cadb41 100644
--- a/third_party/blink/renderer/core/frame/report.h
+++ b/third_party/blink/renderer/core/frame/report.h
@@ -39,6 +39,8 @@
     ScriptWrappable::Trace(visitor);
   }
 
+  ScriptValue toJSON(ScriptState* script_state) const;
+
  private:
   const String type_;
   const String url_;
diff --git a/third_party/blink/renderer/core/frame/report.idl b/third_party/blink/renderer/core/frame/report.idl
index 40d70444..2178073 100644
--- a/third_party/blink/renderer/core/frame/report.idl
+++ b/third_party/blink/renderer/core/frame/report.idl
@@ -10,4 +10,5 @@
     readonly attribute DOMString type;
     readonly attribute DOMString url;
     readonly attribute ReportBody? body;
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/report_body.cc b/third_party/blink/renderer/core/frame/report_body.cc
new file mode 100644
index 0000000..91f8e26d
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/report_body.cc
@@ -0,0 +1,15 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/report_body.h"
+
+namespace blink {
+
+ScriptValue ReportBody::toJSON(ScriptState* script_state) const {
+  V8ObjectBuilder result(script_state);
+  BuildJSONValue(result);
+  return result.GetScriptValue();
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report_body.h b/third_party/blink/renderer/core/frame/report_body.h
index 82a6dae..b1dfeee 100644
--- a/third_party/blink/renderer/core/frame/report_body.h
+++ b/third_party/blink/renderer/core/frame/report_body.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORT_BODY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_REPORT_BODY_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
 
 namespace blink {
@@ -14,6 +15,11 @@
 
  public:
   ~ReportBody() override = default;
+
+  ScriptValue toJSON(ScriptState* script_state) const;
+
+  // This function is public for use in Report::toJSON
+  virtual void BuildJSONValue(V8ObjectBuilder& builder) const = 0;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/report_body.idl b/third_party/blink/renderer/core/frame/report_body.idl
index b11ae3f..022f160 100644
--- a/third_party/blink/renderer/core/frame/report_body.idl
+++ b/third_party/blink/renderer/core/frame/report_body.idl
@@ -7,4 +7,5 @@
 [
     NoInterfaceObject
 ] interface ReportBody {
+    [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/renderer/core/frame/test_report_body.cc b/third_party/blink/renderer/core/frame/test_report_body.cc
new file mode 100644
index 0000000..99c0eba
--- /dev/null
+++ b/third_party/blink/renderer/core/frame/test_report_body.cc
@@ -0,0 +1,13 @@
+// Copyright 2019 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.
+
+#include "third_party/blink/renderer/core/frame/test_report_body.h"
+
+namespace blink {
+
+void TestReportBody::BuildJSONValue(V8ObjectBuilder& builder) const {
+  builder.AddString("message", message());
+}
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/core/frame/test_report_body.h b/third_party/blink/renderer/core/frame/test_report_body.h
index 4ce9240..7e60bc8e 100644
--- a/third_party/blink/renderer/core/frame/test_report_body.h
+++ b/third_party/blink/renderer/core/frame/test_report_body.h
@@ -5,6 +5,7 @@
 #ifndef THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_TEST_REPORT_BODY_H_
 #define THIRD_PARTY_BLINK_RENDERER_CORE_FRAME_TEST_REPORT_BODY_H_
 
+#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
 #include "third_party/blink/renderer/core/frame/report_body.h"
 
 namespace blink {
@@ -19,6 +20,8 @@
 
   String message() const { return message_; }
 
+  void BuildJSONValue(V8ObjectBuilder& builder) const override;
+
  private:
   const String message_;
 };
diff --git a/third_party/blink/renderer/core/frame/test_report_body.idl b/third_party/blink/renderer/core/frame/test_report_body.idl
index 2bffa24..afbd820 100644
--- a/third_party/blink/renderer/core/frame/test_report_body.idl
+++ b/third_party/blink/renderer/core/frame/test_report_body.idl
@@ -8,4 +8,5 @@
     NoInterfaceObject
 ] interface TestReportBody : ReportBody {
   readonly attribute DOMString message;
+  [CallWith=ScriptState] object toJSON();
 };
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
index baa866d0..ef365e06 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/oversized-images-reporting.html
@@ -3,6 +3,7 @@
     <head>
         <script src='/resources/testharness.js'></script>
         <script src='/resources/testharnessreport.js'></script>
+        <script src='../resources/feature-policy-report-json.js'></script>
     </head>
     <body>
         <img src="./oversized.jpg" alt="oversized image" width="50" height="50">
@@ -19,6 +20,7 @@
                     assert_equals(rbody.sourceFile, document.getElementsByTagName('img')[0].src);
                     assert_equals(rbody.lineNumber, null);
                     assert_equals(rbody.columnNumber, null);
+                    check_report_json(reports[0]);
                 }),
                 {types: ['feature-policy-violation'], buffered: true}
             ).observe();
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
index 6655210..80d0b0f 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/payment-reporting.https.html
@@ -3,6 +3,7 @@
   <head>
     <script src='/resources/testharness.js'></script>
     <script src='/resources/testharnessreport.js'></script>
+    <script src='../resources/feature-policy-report-json.js'></script>
   </head>
   <body>
     <script>
@@ -17,6 +18,7 @@
   assert_equals(typeof report.body.lineNumber, "number");
   assert_equals(typeof report.body.columnNumber, "number");
   assert_equals(report.body.disposition, "enforce");
+  check_report_json(report);
 };
 
 new ReportingObserver(t.step_func_done(check_report_format),
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
index d92a685..9e526b90 100644
--- a/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/reporting/sync-xhr-reporting.html
@@ -3,6 +3,7 @@
   <head>
     <script src='/resources/testharness.js'></script>
     <script src='/resources/testharnessreport.js'></script>
+    <script src='../resources/feature-policy-report-json.js'></script>
   </head>
   <body>
     <script>
@@ -17,6 +18,7 @@
   assert_equals(typeof report.body.lineNumber, "number");
   assert_equals(typeof report.body.columnNumber, "number");
   assert_equals(report.body.disposition, "enforce");
+  check_report_json(report);
 };
 
 new ReportingObserver(t.step_func_done(check_report_format),
diff --git a/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js
new file mode 100644
index 0000000..08a0ecad
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/feature-policy/resources/feature-policy-report-json.js
@@ -0,0 +1,20 @@
+/**
+ * @fileoverview functions for ensuring feature policy report is serializable
+ */
+
+const check_report_json = (report) => {
+  // Ensures toJSON method exists on report.
+  assert_equals(typeof report.toJSON, "function");
+  const report_json = report.toJSON();
+  // Ensures toJSON() call is successful.
+  assert_equals(report.type, report_json.type);
+  assert_equals(report.url, report_json.url);
+  assert_equals(report.body.featureId, report_json.body.featureId);
+  assert_equals(report.body.disposition, report_json.body.disposition);
+  assert_equals(report.body.sourceFile, report_json.body.sourceFile);
+  assert_equals(report.body.lineNumber, report_json.body.lineNumber);
+  assert_equals(report.body.columnNumber, report_json.body.columnNumber);
+  // Ensures JSON.stringify() serializes the report correctly.
+  assert_false(JSON.stringify(report) === "{}");
+  assert_equals(JSON.stringify(report), JSON.stringify(report_json));
+}
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
index e3c273568..f47ea45c 100644
--- a/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
+++ b/third_party/blink/web_tests/external/wpt/reporting/generateTestReport.html
@@ -16,6 +16,13 @@
         assert_equals(reports[0].type, "test");
         assert_true(reports[0].url.endsWith("reporting/generateTestReport.html"));
         assert_equals(reports[0].body.message, "Test message.");
+        // Ensure that the toJSON() call of the report are valid.
+        const reportJSON = reports[0].toJSON();
+        assert_equals(reports[0].type, reportJSON.type);
+        assert_equals(reports[0].url, reportJSON.url);
+        assert_equals(reports[0].body.message, reportJSON.body.message);
+        // Ensure that report can be successfully JSON serialized.
+        assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
       });
       test.done();
     });
diff --git a/third_party/blink/web_tests/http/tests/reporting-observer/csp.php b/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
index 0df72aa75..0df988f 100644
--- a/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
+++ b/third_party/blink/web_tests/http/tests/reporting-observer/csp.php
@@ -30,6 +30,24 @@
       assert_equals(reports[0].body.statusCode, 200);
       assert_equals(reports[0].body.lineNumber, null);
       assert_equals(reports[0].body.columnNumber, null);
+      // Ensure the toJSON call is successful.
+      const reportJSON = reports[0].toJSON();
+      assert_equals(reportJSON.type, reports[0].type);
+      assert_equals(reportJSON.url, reports[0].url);
+      assert_equals(typeof reportJSON.body, "object");
+      assert_equals(reportJSON.body.documentURL, reports[0].body.documentURL);
+      assert_equals(reportJSON.body.referrer, reports[0].body.referrer);
+      assert_equals(reportJSON.body.blockedURL, reports[0].body.blockedURL);
+      assert_equals(reportJSON.body.effectiveDirective, reports[0].body.effectiveDirective);
+      assert_equals(reportJSON.body.originalPolicy, reports[0].body.originalPolicy);
+      assert_equals(reportJSON.body.sourceFile, reports[0].body.sourceFile);
+      assert_equals(reportJSON.body.sample, reports[0].body.sample);
+      assert_equals(reportJSON.body.disposition, reports[0].body.disposition);
+      assert_equals(reportJSON.body.statusCode, reports[0].body.statusCode);
+      assert_equals(reportJSON.body.lineNumber, reports[0].body.lineNumber);
+      assert_equals(reportJSON.body.columnNumber, reports[0].body.columnNumber);
+      // Ensure that report can be successfully JSON serialized.
+      assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
     });
 
     test.done();
diff --git a/third_party/blink/web_tests/reporting-observer/intervention.html b/third_party/blink/web_tests/reporting-observer/intervention.html
index 8e12d888..043d3ed 100644
--- a/third_party/blink/web_tests/reporting-observer/intervention.html
+++ b/third_party/blink/web_tests/reporting-observer/intervention.html
@@ -26,6 +26,20 @@
       assert_equals(typeof reports[0].body.lineNumber, "number");
       assert_equals(typeof reports[0].body.columnNumber, "number");
       assert_equals(typeof reports[0].body.message, "string");
+
+      // Ensure the toJSON call is successful.
+      const reportJSON = reports[0].toJSON();
+      assert_equals(reportJSON.type, reports[0].type);
+      assert_equals(reportJSON.url, reports[0].url);
+      assert_equals(typeof reportJSON.body, "object");
+      assert_equals(reportJSON.body.id, reports[0].body.id);
+      assert_equals(reportJSON.body.message, reports[0].body.message);
+      assert_equals(reportJSON.body.sourceFile, reports[0].body.sourceFile);
+      assert_equals(reportJSON.body.lineNumber, reports[0].body.lineNumber);
+      assert_equals(reportJSON.body.columnNumber, reports[0].body.columnNumber);
+      // Ensure that report can be successfully JSON serialized.
+      assert_false(JSON.stringify(reports[0]) === "{}");
+      assert_equals(JSON.stringify(reports[0]), JSON.stringify(reportJSON));
     });
 
     test.done();
diff --git a/third_party/blink/web_tests/reporting-observer/resources/deprecation.js b/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
index 37a6724..ef2cf94 100644
--- a/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
+++ b/third_party/blink/web_tests/reporting-observer/resources/deprecation.js
@@ -20,6 +20,23 @@
             "reporting-observer/resources/deprecation.js"));
         assert_equals(typeof report.body.lineNumber, "number");
         assert_equals(typeof report.body.columnNumber, "number");
+        // Ensure the toJSON call is successful.
+        const reportJSON = report.toJSON();
+        assert_equals(reportJSON.type, report.type);
+        assert_equals(reportJSON.url, report.url);
+        assert_equals(typeof reportJSON.body, "object");
+        assert_equals(reportJSON.body.id, report.body.id);
+        assert_equals(reportJSON.body.message, report.body.message);
+        assert_equals(reportJSON.body.sourceFile, report.body.sourceFile);
+        assert_equals(reportJSON.body.lineNumber, report.body.lineNumber);
+        assert_equals(reportJSON.body.columnNumber, report.body.columnNumber);
+        assert_equals(
+          JSON.stringify(reportJSON.body.anticipatedRemoval),
+          JSON.stringify(report.body.anticipatedRemoval)
+        );
+        // Ensure that report can be successfully JSON serialized.
+        assert_false(JSON.stringify(report) === "{}");
+        assert_equals(JSON.stringify(report), JSON.stringify(reportJSON));
       }
       assert_not_equals(reports[0].body.id, reports[1].body.id);
     });