Adding "is_standardized" flag to RTCStatsMember.

This will allow us to add unstandardized stats for the benefit of
native applications, and easily filter them out in chromium (without
having to maintain a whitelist that lists out every member
individually).

Unstandardized stats are declared as "RTCNonStandardStatsMember",
to make it clear in the declaration (in rtcstats_objects.h) whether
something is standardized or not.

Bug: webrtc:9410
Change-Id: I7c9804c261b7af96738e94dadeaa4b8a56b9ef2c
Reviewed-on: https://webrtc-review.googlesource.com/83743
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23760}
diff --git a/api/stats/rtcstats.h b/api/stats/rtcstats.h
index 1ca7b2c..1705f6ab 100644
--- a/api/stats/rtcstats.h
+++ b/api/stats/rtcstats.h
@@ -211,6 +211,9 @@
   virtual bool is_sequence() const = 0;
   virtual bool is_string() const = 0;
   bool is_defined() const { return is_defined_; }
+  // Is this part of the stats spec? Used so that chromium can easily filter
+  // out anything unstandardized.
+  virtual bool is_standardized() const = 0;
   // Type and value comparator. The names are not compared. These operators are
   // exposed for testing.
   virtual bool operator==(const RTCStatsMemberInterface& other) const = 0;
@@ -249,11 +252,12 @@
   static const Type kType;
 
   explicit RTCStatsMember(const char* name)
-      : RTCStatsMemberInterface(name, false), value_() {}
+      : RTCStatsMemberInterface(name, /*is_defined=*/false), value_() {}
   RTCStatsMember(const char* name, const T& value)
-      : RTCStatsMemberInterface(name, true), value_(value) {}
+      : RTCStatsMemberInterface(name, /*is_defined=*/true), value_(value) {}
   RTCStatsMember(const char* name, T&& value)
-      : RTCStatsMemberInterface(name, true), value_(std::move(value)) {}
+      : RTCStatsMemberInterface(name, /*is_defined=*/true),
+        value_(std::move(value)) {}
   explicit RTCStatsMember(const RTCStatsMember<T>& other)
       : RTCStatsMemberInterface(other.name_, other.is_defined_),
         value_(other.value_) {}
@@ -264,8 +268,9 @@
   Type type() const override { return kType; }
   bool is_sequence() const override;
   bool is_string() const override;
+  bool is_standardized() const override { return true; }
   bool operator==(const RTCStatsMemberInterface& other) const override {
-    if (type() != other.type())
+    if (type() != other.type() || is_standardized() != other.is_standardized())
       return false;
     const RTCStatsMember<T>& other_t =
         static_cast<const RTCStatsMember<T>&>(other);
@@ -291,7 +296,10 @@
   }
   T& operator=(const RTCStatsMember<T>& other) {
     RTC_DCHECK(other.is_defined_);
-    value_ = other.is_defined_;
+    // Shouldn't be attempting to assign an RTCNonStandardStatsMember to an
+    // RTCStatsMember or vice versa.
+    RTC_DCHECK(is_standardized() == other.is_standardized());
+    value_ = other.value_;
     is_defined_ = true;
     return value_;
   }
@@ -320,6 +328,26 @@
   T value_;
 };
 
+// Same as above, but "is_standardized" returns false.
+//
+// Using inheritance just so that it's obvious from the member's declaration
+// whether it's standardized or not.
+template <typename T>
+class RTCNonStandardStatsMember : public RTCStatsMember<T> {
+ public:
+  explicit RTCNonStandardStatsMember(const char* name)
+      : RTCStatsMember<T>(name) {}
+  RTCNonStandardStatsMember(const char* name, const T& value)
+      : RTCStatsMember<T>(name, value) {}
+  RTCNonStandardStatsMember(const char* name, T&& value)
+      : RTCStatsMember<T>(name, std::move(value)) {}
+  explicit RTCNonStandardStatsMember(const RTCNonStandardStatsMember<T>& other)
+      : RTCStatsMember<T>(other) {}
+  explicit RTCNonStandardStatsMember(RTCNonStandardStatsMember<T>&& other)
+      : RTCStatsMember<T>(std::move(other)) {}
+
+  bool is_standardized() const override { return false; }
+};
 }  // namespace webrtc
 
 #endif  // API_STATS_RTCSTATS_H_
diff --git a/stats/rtcstats_unittest.cc b/stats/rtcstats_unittest.cc
index a3f005d..de3e18c 100644
--- a/stats/rtcstats_unittest.cc
+++ b/stats/rtcstats_unittest.cc
@@ -330,6 +330,13 @@
   std::cout << stats.ToJson() << std::endl;
 }
 
+TEST(RTCStatsTest, IsStandardized) {
+  RTCStatsMember<int32_t> standardized("standardized");
+  RTCNonStandardStatsMember<int32_t> unstandardized("unstandardized");
+  EXPECT_TRUE(standardized.is_standardized());
+  EXPECT_FALSE(unstandardized.is_standardized());
+}
+
 // Death tests.
 // Disabled on Android because death tests misbehave on Android, see
 // base/test/gtest_util.h.