summaryrefslogtreecommitdiff
path: root/dom/performance/PerformanceObserver.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/performance/PerformanceObserver.cpp')
-rw-r--r--dom/performance/PerformanceObserver.cpp230
1 files changed, 205 insertions, 25 deletions
diff --git a/dom/performance/PerformanceObserver.cpp b/dom/performance/PerformanceObserver.cpp
index 26f93e8fc1..5482c1f2a6 100644
--- a/dom/performance/PerformanceObserver.cpp
+++ b/dom/performance/PerformanceObserver.cpp
@@ -9,6 +9,7 @@
#include "mozilla/dom/PerformanceBinding.h"
#include "mozilla/dom/PerformanceEntryBinding.h"
#include "mozilla/dom/PerformanceObserverBinding.h"
+#include "nsIScriptError.h"
#include "nsPIDOMWindow.h"
#include "nsQueryObject.h"
#include "nsString.h"
@@ -27,12 +28,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(PerformanceObserver)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallback)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueuedEntries)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(PerformanceObserver)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallback)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueuedEntries)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(PerformanceObserver)
@@ -43,10 +46,14 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserver)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
+const char UnsupportedEntryTypesIgnoredMsgId[] = "UnsupportedEntryTypesIgnored";
+const char AllEntryTypesIgnoredMsgId[] = "AllEntryTypesIgnored";
+
PerformanceObserver::PerformanceObserver(nsPIDOMWindowInner* aOwner,
PerformanceObserverCallback& aCb)
: mOwner(aOwner)
, mCallback(&aCb)
+ , mObserverType(ObserverTypeUndefined)
, mConnected(false)
{
MOZ_ASSERT(mOwner);
@@ -56,6 +63,7 @@ PerformanceObserver::PerformanceObserver(nsPIDOMWindowInner* aOwner,
PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate,
PerformanceObserverCallback& aCb)
: mCallback(&aCb)
+ , mObserverType(ObserverTypeUndefined)
, mConnected(false)
{
MOZ_ASSERT(aWorkerPrivate);
@@ -115,7 +123,8 @@ PerformanceObserver::Notify()
mQueuedEntries.Clear();
ErrorResult rv;
- mCallback->Call(this, *list, *this, rv);
+ RefPtr<PerformanceObserverCallback> callback(mCallback);
+ callback->Call(this, *list, *this, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
@@ -126,64 +135,228 @@ PerformanceObserver::QueueEntry(PerformanceEntry* aEntry)
{
MOZ_ASSERT(aEntry);
- nsAutoString entryType;
- aEntry->GetEntryType(entryType);
- if (!mEntryTypes.Contains<nsString>(entryType)) {
+ if (!ObservesTypeOfEntry(aEntry)) {
return;
}
mQueuedEntries.AppendElement(aEntry);
}
+/*
+ * Keep this list in alphabetical order.
+ * https://w3c.github.io/performance-timeline/#supportedentrytypes-attribute
+ */
static const char16_t *const sValidTypeNames[4] = {
u"mark",
u"measure",
- u"resource",
- u"server"
+ u"navigation",
+ u"resource"
};
void
-PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
- ErrorResult& aRv)
-{
- if (aOptions.mEntryTypes.IsEmpty()) {
- aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+PerformanceObserver::ReportUnsupportedTypesErrorToConsole(
+ bool aIsMainThread, const char* msgId, const nsString& aInvalidTypes) {
+ if (!aIsMainThread) {
+ nsTArray<nsString> params;
+ params.AppendElement(aInvalidTypes);
+ WorkerPrivate::ReportErrorToConsole(msgId, params);
+ } else {
+ nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(mOwner);
+ nsIDocument* document = ownerWindow->GetExtantDoc();
+ const char16_t* params[] = {aInvalidTypes.get()};
+ nsContentUtils::ReportToConsole(
+ nsIScriptError::warningFlag, NS_LITERAL_CSTRING("DOM"), document,
+ nsContentUtils::eDOM_PROPERTIES, msgId, params, 1);
+ }
+ return;
+}
+
+void PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
+ ErrorResult& aRv) {
+ const Optional<Sequence<nsString>>& maybeEntryTypes = aOptions.mEntryTypes;
+ const Optional<nsString>& maybeType = aOptions.mType;
+ const Optional<bool>& maybeBuffered = aOptions.mBuffered;
+
+ if (!mPerformance) {
+ aRv.Throw(NS_ERROR_FAILURE);
return;
}
- nsTArray<nsString> validEntryTypes;
+ if (!maybeEntryTypes.WasPassed() && !maybeType.WasPassed()) {
+ /* Per spec (3.3.1.2), this should be a syntax error. */
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
- for (const char16_t* name : sValidTypeNames) {
- nsDependentString validTypeName(name);
- if (aOptions.mEntryTypes.Contains<nsString>(validTypeName) &&
- !validEntryTypes.Contains<nsString>(validTypeName)) {
- validEntryTypes.AppendElement(validTypeName);
+ if (maybeEntryTypes.WasPassed() &&
+ (maybeType.WasPassed() || maybeBuffered.WasPassed())) {
+ /* Per spec (3.3.1.3), this, too, should be a syntax error. */
+ aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+ return;
+ }
+
+ /* 3.3.1.4.1 */
+ if (mObserverType == ObserverTypeUndefined) {
+ if (maybeEntryTypes.WasPassed()) {
+ mObserverType = ObserverTypeMultiple;
+ } else {
+ mObserverType = ObserverTypeSingle;
}
}
- if (validEntryTypes.IsEmpty()) {
- aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ /* 3.3.1.4.2 */
+ if (mObserverType == ObserverTypeSingle && maybeEntryTypes.WasPassed()) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_MODIFICATION_ERR);
+ return;
+ }
+ /* 3.3.1.4.3 */
+ if (mObserverType == ObserverTypeMultiple && maybeType.WasPassed()) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_MODIFICATION_ERR);
return;
}
- mEntryTypes.SwapElements(validEntryTypes);
+ /* 3.3.1.5 */
+ if (mObserverType == ObserverTypeMultiple) {
+ const Sequence<nsString>& entryTypes = maybeEntryTypes.Value();
- mPerformance->AddObserver(this);
+ if (entryTypes.IsEmpty()) {
+ return;
+ }
+
+ /* 3.3.1.5.2 */
+ nsTArray<nsString> validEntryTypes;
+ for (const char16_t* name : sValidTypeNames) {
+ nsDependentString validTypeName(name);
+ if (entryTypes.Contains<nsString>(validTypeName) &&
+ !validEntryTypes.Contains<nsString>(validTypeName)) {
+ validEntryTypes.AppendElement(validTypeName);
+ }
+ }
+
+ nsAutoString invalidTypesJoined;
+ bool addComma = false;
+ for (const auto& type : entryTypes) {
+ if (!validEntryTypes.Contains<nsString>(type)) {
+ if (addComma) {
+ invalidTypesJoined.AppendLiteral(", ");
+ }
+ addComma = true;
+ invalidTypesJoined.Append(type);
+ }
+ }
+
+ if (!invalidTypesJoined.IsEmpty()) {
+ ReportUnsupportedTypesErrorToConsole(NS_IsMainThread(),
+ UnsupportedEntryTypesIgnoredMsgId,
+ invalidTypesJoined);
+ }
+
+ /* 3.3.1.5.3 */
+ if (validEntryTypes.IsEmpty()) {
+ nsString errorString;
+ ReportUnsupportedTypesErrorToConsole(
+ NS_IsMainThread(), AllEntryTypesIgnoredMsgId, errorString);
+ return;
+ }
+
+ /*
+ * Registered or not, we clear out the list of options, and start fresh
+ * with the one that we are using here. (3.3.1.5.4,5)
+ */
+ mOptions.Clear();
+ mOptions.AppendElement(aOptions);
+
+ } else {
+ MOZ_ASSERT(mObserverType == ObserverTypeSingle);
+ bool typeValid = false;
+ nsString type = maybeType.Value();
+
+ /* 3.3.1.6.2 */
+ for (const char16_t* name : sValidTypeNames) {
+ nsDependentString validTypeName(name);
+ if (type == validTypeName) {
+ typeValid = true;
+ break;
+ }
+ }
+
+ if (!typeValid) {
+ ReportUnsupportedTypesErrorToConsole(
+ NS_IsMainThread(), UnsupportedEntryTypesIgnoredMsgId, type);
+ return;
+ }
+
+ /* 3.3.1.6.4, 3.3.1.6.4 */
+ bool didUpdateOptionsList = false;
+ nsTArray<PerformanceObserverInit> updatedOptionsList;
+ for (auto& option : mOptions) {
+ if (option.mType.WasPassed() && option.mType.Value() == type) {
+ updatedOptionsList.AppendElement(aOptions);
+ didUpdateOptionsList = true;
+ } else {
+ updatedOptionsList.AppendElement(option);
+ }
+ }
+ if (!didUpdateOptionsList) {
+ updatedOptionsList.AppendElement(aOptions);
+ }
+ mOptions.SwapElements(updatedOptionsList);
- if (aOptions.mBuffered) {
- for (auto entryType : mEntryTypes) {
+ /* 3.3.1.6.5 */
+ if (maybeBuffered.WasPassed() && maybeBuffered.Value()) {
nsTArray<RefPtr<PerformanceEntry>> existingEntries;
- mPerformance->GetEntriesByType(entryType, existingEntries);
+ mPerformance->GetEntriesByType(type, existingEntries);
if (!existingEntries.IsEmpty()) {
mQueuedEntries.AppendElements(existingEntries);
}
}
}
-
+ /* Add ourselves to the list of registered performance
+ * observers, if necessary. (3.3.1.5.4,5; 3.3.1.6.4)
+ */
+ mPerformance->AddObserver(this);
mConnected = true;
}
void
+PerformanceObserver::GetSupportedEntryTypes(const GlobalObject& aGlobal, JS::MutableHandle<JSObject*> aObject)
+{
+ nsTArray<nsString> validTypes;
+ JS::Rooted<JS::Value> val(aGlobal.Context());
+
+ for (const char16_t* name : sValidTypeNames) {
+ nsString validTypeName(name);
+ validTypes.AppendElement(validTypeName);
+ }
+
+ if (!ToJSValue(aGlobal.Context(), validTypes, &val)) {
+ /*
+ * If this conversion fails, we don't set a result.
+ * The spec does not allow us to throw an exception.
+ */
+ return;
+ }
+ aObject.set(&val.toObject());
+}
+
+bool
+PerformanceObserver::ObservesTypeOfEntry(PerformanceEntry* aEntry)
+{
+ for (auto& option : mOptions) {
+ if (option.mType.WasPassed()) {
+ if (option.mType.Value() == aEntry->GetEntryType()) {
+ return true;
+ }
+ } else {
+ if (option.mEntryTypes.Value().Contains(aEntry->GetEntryType())) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
PerformanceObserver::Disconnect()
{
if (mConnected) {
@@ -192,3 +365,10 @@ PerformanceObserver::Disconnect()
mConnected = false;
}
}
+
+void
+PerformanceObserver::TakeRecords(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
+{
+ MOZ_ASSERT(aRetval.IsEmpty());
+ aRetval.SwapElements(mQueuedEntries);
+}