Bug 902003: getStats API skeleton. r=jesup, smaug
authorJan-Ivar Bruaroey <jib@mozilla.com>
Thu, 17 Oct 2013 18:00:05 -0400
changeset 151177 5372aea57cdb429e4fcf6a951e2ae1e754d64034
parent 151176 1945cbcef58f10a6d8a428954d1f5847eba3ecb5
child 151178 2a6d4fa91d5b89fc536536b2bbce0da7bd392dd4
push id25479
push usercbook@mozilla.com
push dateFri, 18 Oct 2013 09:36:13 +0000
treeherderautoland@2def80d5a106 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, smaug
bugs902003
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 902003: getStats API skeleton. r=jesup, smaug
dom/media/PeerConnection.js
dom/media/PeerConnection.manifest
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/PeerConnectionImpl.webidl
dom/webidl/PeerConnectionObserver.webidl
dom/webidl/RTCPeerConnection.webidl
dom/webidl/RTCStatsReport.webidl
dom/webidl/moz.build
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -9,22 +9,24 @@ const {classes: Cc, interfaces: Ci, util
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
 const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
 const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
 const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
 const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
+const PC_STATS_CONTRACT = "@mozilla.org/dom/rtcstatsreport;1";
 
-const PC_CID = Components.ID("{9878b414-afaa-4176-a887-1e02b3b047c2}");
+const PC_CID = Components.ID("{fc684a5c-c729-42c7-aa82-3c10dc4398f3}");
 const PC_OBS_CID = Components.ID("{1d44a18e-4545-4ff3-863d-6dbd6234a583}");
 const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
 const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
 const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
+const PC_STATS_CID = Components.ID("{7fe6e18b-0da3-4056-bf3b-440ef3809e06}");
 
 // Global list of PeerConnection objects, so they can be cleaned up when
 // a page is torn down. (Maps inner window ID to an array of PC objects).
 function GlobalPCList() {
   this._list = [];
   this._networkdown = false; // XXX Need to query current state somehow
   Services.obs.addObserver(this, "inner-window-destroyed", true);
   Services.obs.addObserver(this, "profile-change-net-teardown", true);
@@ -147,27 +149,69 @@ RTCSessionDescription.prototype = {
   init: function(win) { this._win = win; },
 
   __init: function(dict) {
     this.type = dict.type;
     this.sdp  = dict.sdp;
   }
 };
 
+function RTCStatsReport(win, report) {
+  this._win = win;
+  this.report = report;
+}
+RTCStatsReport.prototype = {
+  classDescription: "RTCStatsReport",
+  classID: PC_STATS_CID,
+  contractID: PC_STATS_CONTRACT,
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
+                                         Ci.nsIDOMGlobalPropertyInitializer]),
+
+  forEach: function(cb, thisArg) {
+    for (var key in this.report) {
+      if (this.report.hasOwnProperty(key)) {
+        cb.call(thisArg || this, this.get(key), key, this.report);
+      }
+    }
+  },
+
+  get: function(key) {
+    function publify(win, obj) {
+      let props = {};
+      for (let k in obj) {
+        props[k] = {enumerable:true, configurable:true, writable:true, value:obj[k]};
+      }
+      let pubobj = Cu.createObjectIn(win);
+      Object.defineProperties(pubobj, props);
+      Cu.makeObjectPropsNormal(pubobj);
+      return pubobj;
+    }
+
+    // Return a content object rather than a wrapped chrome one.
+    return publify(this._win, this.report[key]);
+  },
+
+  has: function(key) {
+    return this.report[key] !== undefined;
+  }
+};
+
 function RTCPeerConnection() {
   this._queue = [];
 
   this._pc = null;
   this._observer = null;
   this._closed = false;
 
   this._onCreateOfferSuccess = null;
   this._onCreateOfferFailure = null;
   this._onCreateAnswerSuccess = null;
   this._onCreateAnswerFailure = null;
+  this._onGetStatsSuccess = null;
+  this._onGetStatsFailure = null;
 
   this._pendingType = null;
   this._localType = null;
   this._remoteType = null;
   this._trickleIce = false;
 
   /**
    * Everytime we get a request from content, we put it in the queue. If
@@ -734,16 +778,31 @@ RTCPeerConnection.prototype = {
         break;
       case Ci.IPeerConnection.kClosed:
         state = "closed";
         break;
     }
     return state;
   },
 
+  getStats: function(selector, onSuccess, onError) {
+    this._onGetStatsSuccess = onSuccess;
+    this._onGetStatsFailure = onError;
+
+    this._queueOrRun({
+      func: this._getStats,
+      args: [selector],
+      wait: true
+    });
+  },
+
+  _getStats: function(selector) {
+    this._getPC().getStats(selector);
+  },
+
   createDataChannel: function(label, dict) {
     this._checkClosed();
     if (dict == undefined) {
       dict = {};
     }
     if (dict.maxRetransmitNum != undefined) {
       dict.maxRetransmits = dict.maxRetransmitNum;
       this.reportWarning("Deprecated RTCDataChannelInit dictionary entry maxRetransmitNum used!", null, 0);
@@ -1066,16 +1125,48 @@ PeerConnectionObserver.prototype = {
         break;
 
       default:
         this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
         break;
     }
   },
 
+  onGetStatsSuccess: function(dict) {
+    function appendStats(stats, report) {
+      if (stats) {
+        stats.forEach(function(stat) {
+          report[stat.id] = stat;
+        });
+      }
+    }
+
+    let report = {};
+    appendStats(dict.rtpStreamStats, report);
+    appendStats(dict.inboundRTPStreamStats, report);
+    appendStats(dict.outboundRTPStreamStats, report);
+    appendStats(dict.mediaStreamTrackStats, report);
+    appendStats(dict.mediaStreamStats, report);
+    appendStats(dict.transportStats, report);
+    appendStats(dict.iceComponentStats, report);
+    appendStats(dict.iceCandidateStats, report);
+    appendStats(dict.codecStats, report);
+
+    this.callCB(this._dompc._onGetStatsSuccess,
+                this._dompc._win.RTCStatsReport._create(this._dompc._win,
+                                                        new RTCStatsReport(this._dompc._win,
+                                                                           report)));
+    this._dompc._executeNext();
+  },
+
+  onGetStatsError: function(code, message) {
+    this.callCB(this._dompc._onGetStatsFailure, new RTCError(code, message));
+    this._dompc._executeNext();
+  },
+
   onAddStream: function(stream) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
                                                              { stream: stream }));
   },
 
   onRemoveStream: function(stream, type) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
                                                              { stream: stream }));
@@ -1101,10 +1192,10 @@ PeerConnectionObserver.prototype = {
 
   getSupportedConstraints: function(dict) {
     return dict;
   }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
   [GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection,
-   PeerConnectionObserver]
+   RTCStatsReport, PeerConnectionObserver]
 );
--- a/dom/media/PeerConnection.manifest
+++ b/dom/media/PeerConnection.manifest
@@ -1,11 +1,13 @@
-component {9878b414-afaa-4176-a887-1e02b3b047c2} PeerConnection.js
+component {fc684a5c-c729-42c7-aa82-3c10dc4398f3} PeerConnection.js
 component {1d44a18e-4545-4ff3-863d-6dbd6234a583} PeerConnection.js
 component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
 component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
 component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
+component {7fe6e18b-0da3-4056-bf3b-440ef3809e06} PeerConnection.js
 
-contract @mozilla.org/dom/peerconnection;1 {9878b414-afaa-4176-a887-1e02b3b047c2}
+contract @mozilla.org/dom/peerconnection;1 {fc684a5c-c729-42c7-aa82-3c10dc4398f3}
 contract @mozilla.org/dom/peerconnectionobserver;1 {1d44a18e-4545-4ff3-863d-6dbd6234a583}
 contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
 contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
 contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
+contract @mozilla.org/dom/rtcstatsreport;1 {7fe6e18b-0da3-4056-bf3b-440ef3809e06}
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -385,16 +385,17 @@ var interfaceNamesInGlobalScope =
     {name: "Promise", b2g: false, release: false},
     "PropertyNodeList",
     "Range",
     "RecordErrorEvent",
     "Rect",
     "RGBColor",
     "RTCDataChannelEvent",
     "RTCPeerConnectionIceEvent",
+    "RTCStatsReport",
     "Screen",
     "ScriptProcessorNode",
     "ScrollAreaEvent",
     "Selection",
     "SettingsLock",
     "SettingsManager",
     "SimpleGestureEvent",
     {name: "SimpleTest", xbl: false},
--- a/dom/webidl/PeerConnectionImpl.webidl
+++ b/dom/webidl/PeerConnectionImpl.webidl
@@ -29,16 +29,20 @@ interface PeerConnectionImpl  {
   void createOffer(optional MediaConstraintsInternal constraints);
   [Throws]
   void createAnswer(optional MediaConstraintsInternal constraints);
   [Throws]
   void setLocalDescription(long action, DOMString sdp);
   [Throws]
   void setRemoteDescription(long action, DOMString sdp);
 
+  /* Stats call */
+  [Throws]
+  void getStats(MediaStreamTrack? selector);
+
   /* Adds the stream created by GetUserMedia */
   [Throws]
   void addStream(MediaStream stream);
   [Throws]
   void removeStream(MediaStream stream);
   [Throws]
   void closeStreams();
 
--- a/dom/webidl/PeerConnectionObserver.webidl
+++ b/dom/webidl/PeerConnectionObserver.webidl
@@ -17,16 +17,20 @@ interface PeerConnectionObserver
   void onSetLocalDescriptionSuccess();
   void onSetRemoteDescriptionSuccess();
   void onSetLocalDescriptionError(unsigned long name, DOMString message);
   void onSetRemoteDescriptionError(unsigned long name, DOMString message);
   void onAddIceCandidateSuccess();
   void onAddIceCandidateError(unsigned long name, DOMString message);
   void onIceCandidate(unsigned short level, DOMString mid, DOMString candidate);
 
+  /* Stats callbacks */
+  void onGetStatsSuccess(optional RTCStatsReportInternal report);
+  void onGetStatsError(unsigned long name, DOMString message);
+
   /* Data channel callbacks */
   void notifyDataChannel(DataChannel channel);
   void notifyConnection();
   void notifyClosedConnection();
 
   /* Notification of one of several types of state changed */
   void onStateChange(PCObserverStateType state);
 
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -5,16 +5,17 @@
  *
  * The origin of this IDL file is
  * http://dev.w3.org/2011/webrtc/editor/webrtc.html#idl-def-RTCPeerConnection
  */
 
 callback RTCSessionDescriptionCallback = void (mozRTCSessionDescription sdp);
 callback RTCPeerConnectionErrorCallback = void (DOMString errorInformation);
 callback VoidFunction = void ();
+callback RTCStatsCallback = void (RTCStatsReport report);
 
 enum RTCSignalingState {
     "stable",
     "have-local-offer",
     "have-remote-offer",
     "have-local-pranswer",
     "have-remote-pranswer",
     "closed"
@@ -114,16 +115,20 @@ interface mozRTCPeerConnection : EventTa
   void removeStream (MediaStream stream);
   void close ();
   attribute EventHandler onnegotiationneeded;
   attribute EventHandler onicecandidate;
   attribute EventHandler onsignalingstatechange;
   attribute EventHandler onaddstream;
   attribute EventHandler onremovestream;
   attribute EventHandler oniceconnectionstatechange;
+
+  void getStats (MediaStreamTrack? selector,
+                 RTCStatsCallback successCallback,
+                 RTCPeerConnectionErrorCallback failureCallback);
 };
 
 // Mozilla extensions.
 partial interface mozRTCPeerConnection {
   // Deprecated callbacks (use causes warning)
   attribute RTCPeerConnectionErrorCallback onicechange;
   attribute RTCPeerConnectionErrorCallback ongatheringchange;
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/RTCStatsReport.webidl
@@ -0,0 +1,119 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://dev.w3.org/2011/webrtc/editor/webrtc.html#rtcstatsreport-object
+ */
+
+enum RTCStatsType {
+  "inbound-rtp",
+  "outbound-rtp"
+};
+
+dictionary RTCStats {
+  DOMHighResTimeStamp timestamp;
+  RTCStatsType type;
+  DOMString id;
+};
+
+dictionary RTCRTPStreamStats : RTCStats {
+  DOMString ssrc;
+  DOMString remoteId;
+  DOMString mediaTrackId;
+  DOMString transportId;
+  DOMString codecId;
+};
+
+dictionary RTCInboundRTPStreamStats : RTCRTPStreamStats {
+  unsigned long packetsReceived;
+  unsigned long bytesReceived;
+  float jitter;
+};
+
+dictionary RTCOutboundRTPStreamStats : RTCRTPStreamStats {
+  unsigned long packetsSent;
+  unsigned long bytesSent;
+};
+
+dictionary RTCMediaStreamTrackStats : RTCStats {
+  DOMString trackIdentifier;      // track.id property
+  boolean remoteSource;
+  sequence<DOMString> ssrcIds;
+  unsigned long audioLevel;       // Only for audio, the rest are only for video
+  unsigned long frameWidth;
+  unsigned long frameHeight;
+  unsigned long framesPerSecond;  // The nominal FPS value
+  unsigned long framesSent;
+  unsigned long framesReceived;   // Only for remoteSource=true
+  unsigned long framesDecoded;
+  long first;
+};
+
+dictionary RTCMediaStreamStats : RTCStats {
+  DOMString streamIdentifier;     // stream.id property
+  sequence<DOMString> trackIds;   // Note: stats object ids, not track.id
+};
+
+dictionary RTCTransportStats: RTCStats {
+  unsigned long bytesSent;
+  unsigned long bytesReceived;
+};
+
+dictionary RTCIceComponentStats : RTCStats {
+  DOMString transportId;
+  long component;
+  unsigned long bytesSent;
+  unsigned long bytesReceived;
+  boolean activeConnection;
+};
+
+enum RTCStatsIceCandidateType {
+  "host",
+  "server-reflexive",
+  "peer-reflexive",
+  "relayed"
+};
+
+dictionary RTCIceCandidateStats : RTCStats {
+  DOMString candidateId;
+  DOMString ipAddress;
+  long portNumber;
+  RTCStatsIceCandidateType candidateType;
+};
+
+dictionary RTCCodecStats : RTCStats {
+  unsigned long payloadType;       // As used in RTP encoding.
+  DOMString codec;                 // video/vp8 or equivalent
+  unsigned long clockRate;
+  unsigned long channels;          // 2=stereo, missing for most other cases.
+  DOMString parameters;            // From SDP description line
+};
+
+callback RTCStatsReportCallback = void (object value, DOMString key, RTCStatsReport obj);
+
+// This is the internal representation of the report in this implementation
+// to be received from c++
+
+dictionary RTCStatsReportInternal {
+  sequence<RTCRTPStreamStats>         rtpStreamStats;
+  sequence<RTCInboundRTPStreamStats>  inboundRTPStreamStats;
+  sequence<RTCOutboundRTPStreamStats> outboundRTPStreamStats;
+  sequence<RTCMediaStreamTrackStats>  mediaStreamTrackStats;
+  sequence<RTCMediaStreamStats>       mediaStreamStats;
+  sequence<RTCTransportStats>         transportStats;
+  sequence<RTCIceComponentStats>      iceComponentStats;
+  sequence<RTCIceCandidateStats>      iceCandidateStats;
+  sequence<RTCCodecStats>             codecStats;
+};
+
+[Pref="media.peerconnection.enabled",
+// TODO: Use MapClass here once it's available (Bug 928114)
+// MapClass(DOMString, object)
+ JSImplementation="@mozilla.org/dom/rtcstatsreport;1"]
+interface RTCStatsReport {
+  void forEach(RTCStatsReportCallback callbackFn, optional any thisArg);
+  object get(DOMString key);
+  boolean has(DOMString key);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -254,16 +254,17 @@ WEBIDL_FILES = [
     'ProcessingInstruction.webidl',
     'Promise.webidl',
     'PushManager.webidl',
     'RGBColor.webidl',
     'RTCConfiguration.webidl',
     'RTCIceCandidate.webidl',
     'RTCPeerConnection.webidl',
     'RTCSessionDescription.webidl',
+    'RTCStatsReport.webidl',
     'Range.webidl',
     'Rect.webidl',
     'SVGAElement.webidl',
     'SVGAltGlyphElement.webidl',
     'SVGAngle.webidl',
     'SVGAnimateElement.webidl',
     'SVGAnimateMotionElement.webidl',
     'SVGAnimateTransformElement.webidl',
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1,13 +1,12 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include <string>
 #include <cstdlib>
 #include <cerrno>
 
 #include "base/histogram.h"
 #include "vcm.h"
 #include "CSFLog.h"
 #include "timecard.h"
 #include "ccapi_call_info.h"
@@ -37,16 +36,17 @@
 #include "PeerConnectionCtx.h"
 #include "PeerConnectionImpl.h"
 #include "PeerConnectionMedia.h"
 #include "nsPIDOMWindow.h"
 #include "nsDOMDataChannelDeclarations.h"
 #include "dtlsidentity.h"
 
 #ifdef MOZILLA_INTERNAL_API
+#include "nsPerformance.h"
 #include "nsDOMDataChannel.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Telemetry.h"
 #include "nsDOMJSUtils.h"
 #include "nsIDocument.h"
 #include "nsIScriptError.h"
 #include "nsPrintfCString.h"
 #include "nsURLHelper.h"
@@ -1105,16 +1105,37 @@ PeerConnectionImpl::SetRemoteDescription
   STAMP_TIMECARD(tc, "Set Remote Description");
 
   mRemoteRequestedSDP = aSDP;
   mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action,
                                          mRemoteRequestedSDP, tc);
   return NS_OK;
 }
 
+// WebRTC uses highres time relative to the UNIX epoch (Jan 1, 1970, UTC).
+
+#ifdef MOZILLA_INTERNAL_API
+nsresult
+PeerConnectionImpl::GetTimeSinceEpoch(DOMHighResTimeStamp *result) {
+  MOZ_ASSERT(NS_IsMainThread());
+  nsPerformance *perf = mWindow->GetPerformance();
+  NS_ENSURE_TRUE(perf && perf->Timing(), NS_ERROR_UNEXPECTED);
+  *result = perf->Now() + perf->Timing()->NavigationStart();
+  return NS_OK;
+}
+#endif
+
+NS_IMETHODIMP
+PeerConnectionImpl::GetStats(mozilla::dom::MediaStreamTrack *aSelector) {
+  PC_AUTO_ENTER_API_CALL(true);
+
+  // TODO: Insert dispatch to STS here.
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) {
   PC_AUTO_ENTER_API_CALL(true);
 
   Timecard *tc = mTimeCard;
   mTimeCard = nullptr;
   STAMP_TIMECARD(tc, "Add Ice Candidate");
 
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -55,16 +55,17 @@ class NrIceTurnServer;
 typedef Fake_DOMMediaStream DOMMediaStream;
 #else
 class DOMMediaStream;
 #endif
 
 namespace dom {
 class RTCConfiguration;
 class MediaConstraintsInternal;
+class MediaStreamTrack;
 
 #ifdef USE_FAKE_PCOBSERVER
 typedef test::AFakePCObserver PeerConnectionObserver;
 typedef const char *PCObserverString;
 #else
 class PeerConnectionObserver;
 typedef NS_ConvertUTF8toUTF16 PCObserverString;
 #endif
@@ -287,16 +288,22 @@ public:
   }
 
   NS_IMETHODIMP SetRemoteDescription (int32_t aAction, const char* aSDP);
   void SetRemoteDescription (int32_t aAction, const nsAString& aSDP, ErrorResult &rv)
   {
     rv = SetRemoteDescription(aAction, NS_ConvertUTF16toUTF8(aSDP).get());
   }
 
+  NS_IMETHODIMP_TO_ERRORRESULT(GetStats, ErrorResult &rv,
+                               mozilla::dom::MediaStreamTrack *aSelector)
+  {
+    rv = GetStats(aSelector);
+  }
+
   NS_IMETHODIMP AddIceCandidate(const char* aCandidate, const char* aMid,
                                 unsigned short aLevel);
   void AddIceCandidate(const nsAString& aCandidate, const nsAString& aMid,
                        unsigned short aLevel, ErrorResult &rv)
   {
     rv = AddIceCandidate(NS_ConvertUTF16toUTF8(aCandidate).get(),
                          NS_ConvertUTF16toUTF8(aMid).get(), aLevel);
   }
@@ -455,16 +462,17 @@ private:
     NS_ENSURE_SUCCESS(mThread->IsOnCurrentThread(&on), false);
     NS_ENSURE_TRUE(on, false);
 #endif
     return true;
   }
 
 #ifdef MOZILLA_INTERNAL_API
   void virtualDestroyNSSReference() MOZ_FINAL;
+  nsresult GetTimeSinceEpoch(DOMHighResTimeStamp *result);
 #endif
 
   // Shut down media - called on main thread only
   void ShutdownMedia();
 
   // ICE callbacks run on the right thread.
   nsresult IceStateChange_m(mozilla::dom::PCImplIceState aState);