diff --git a/mozilla/extensions/metrics/public/nsIMetricsService.idl b/mozilla/extensions/metrics/public/nsIMetricsService.idl index dce47637b0e..883ed22329b 100644 --- a/mozilla/extensions/metrics/public/nsIMetricsService.idl +++ b/mozilla/extensions/metrics/public/nsIMetricsService.idl @@ -40,26 +40,130 @@ interface nsIPropertyBag; -[scriptable, uuid(30cad119-e248-4211-9682-8f95efe995c4)] +/** + * This file defines the interfaces for the Metrics Service. + * + * This service allows arbitrary types of events to be logged and uploaded + * to a server, based on server-configured collection parameters. + * The nsIMetricsService API provides an abstraction for the underlying XML + * data format. + * + * For more information about the data format and the built-in + * event collectors, see http://wiki.mozilla.org/Browser_Metrics. + */ + + +/** + * nsIMetricsEventItem represents a particular node of data to record + * in an event. Each item has a namespaced item name, a list of properties + * (key/value pairs), and an ordered list of child items. The child items + * need not be unique; an item may be repeated. + */ +[scriptable, uuid(880b8655-15a4-4b72-b6d4-6e3325bca4b6)] +interface nsIMetricsEventItem : nsISupports +{ + /** + * The namespace for this item, which must be a valid XML namespace URI. + */ + readonly attribute DOMString itemNamespace; + + /** + * The name of this item, which must be a valid XML tag name. + */ + readonly attribute DOMString itemName; + + /** + * A PropertyBag containing the key/value pairs to be included in the item. + * JavaScript callers can simply set this to an object containing the + * key/value pairs as object properties. + */ + attribute nsIPropertyBag properties; + + /** + * Returns the child event item at the given index. + */ + nsIMetricsEventItem childAt(in long index); + + /** + * Returns the first occurrence of the given item in the child list, + * or -1 if the item is not in the child list. + */ + long indexOf(in nsIMetricsEventItem item); + + /** + * Appends a child event item to this item. + */ + void appendChild(in nsIMetricsEventItem item); + + /** + * Inserts a child event item at a given index, moving later items + * up by one position. + * @param item The new item to insert + * @param index The position in the array. If the index is equal to + * childCount, the new item will be appended. + * The index may not be greater than childCount. + */ + void insertChildAt(in nsIMetricsEventItem item, in long index); + + /** + * Removes a child event item at the given index, moving all items + * stored at a higher position down one. + */ + void removeChildAt(in long index); + + /** + * Replaces a child event item at the given index. + * @param newItem The new item + * @param index The position of the item to be replaced + */ + void replaceChildAt(in nsIMetricsEventItem newItem, in long index); + + /** + * Clears all of the child items. + */ + void clearChildren(); + + /** + * The number of child event items + */ + readonly attribute long childCount; +}; + +[scriptable, uuid(ff320b73-ecd4-4e08-a09d-b37537770df6)] interface nsIMetricsService : nsISupports { /** - * Logs an event using the given namespace. The event is recorded with a - * timestamp generated at the time at which this method is called. + * Creates a new EventItem object to hold event data. + * The event will not be logged until logEvent() is called. + * @param itemNamespace The new item's namespace + * @param itemName The new item's name * - * @param eventNS - * The namespace of the event, which must be a valid XML namespace - * URI. - * @param eventName - * The name of the event, which must be a valid XML tag name. - * @param eventValues - * A PropertyBag containing the key/value pairs to be included - * in the event. JavaScript callers can simply pass in an object - * containing the key/value pairs as object properties. - * The "time" key is reserved for the event timestamp. + * @returns a new MetricsEventItem instance */ - void logEvent(in DOMString eventNS, in DOMString event, - in nsIPropertyBag eventValues); + nsIMetricsEventItem createEventItem(in DOMString itemNamespace, + in DOMString itemName); + + /** + * Logs an event using the given EventItem. The event is recorded with a + * timestamp generated at the time at which this method is called, and a + * session id for this instance of the application. The keys "time" and + * "sessionid" are reserved for this data. + * + * @param item + * The item to log. This item and its entire tree of child items + * will be logged as part of the event. + */ + void logEvent(in nsIMetricsEventItem item); + + /** + * Constructs and logs an EventItem, using the given namespace, event name, + * and event properties. This is a more convenient version of logEvent() for + * the case where there are no child EventItems. + * + * @see nsIMetricsEventItem + */ + void logSimpleEvent(in DOMString eventNS, in DOMString event, + in nsIPropertyBag eventValues); /** * Flush data to disk. diff --git a/mozilla/extensions/metrics/src/Makefile.in b/mozilla/extensions/metrics/src/Makefile.in index 8f01c3d36de..21960ddbe45 100644 --- a/mozilla/extensions/metrics/src/Makefile.in +++ b/mozilla/extensions/metrics/src/Makefile.in @@ -65,6 +65,7 @@ REQUIRES = xpcom \ CPPSRCS = \ nsLoadCollector.cpp \ nsMetricsConfig.cpp \ + nsMetricsEventItem.cpp \ nsMetricsModule.cpp \ nsMetricsService.cpp \ nsWindowCollector.cpp \ diff --git a/mozilla/extensions/metrics/src/nsMetricsEventItem.cpp b/mozilla/extensions/metrics/src/nsMetricsEventItem.cpp new file mode 100644 index 00000000000..94a304a0104 --- /dev/null +++ b/mozilla/extensions/metrics/src/nsMetricsEventItem.cpp @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Metrics extension. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsMetricsEventItem.h" +#include "nsIPropertyBag.h" + +nsMetricsEventItem::nsMetricsEventItem(const nsAString &itemNamespace, + const nsAString &itemName) + : mNamespace(itemNamespace), mName(itemName) +{ +} + +nsMetricsEventItem::~nsMetricsEventItem() +{ +} + +NS_IMPL_ISUPPORTS1(nsMetricsEventItem, nsIMetricsEventItem) + +NS_IMETHODIMP +nsMetricsEventItem::GetItemNamespace(nsAString &result) +{ + result = mNamespace; + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::GetItemName(nsAString &result) +{ + result = mName; + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::GetProperties(nsIPropertyBag **aProperties) +{ + NS_IF_ADDREF(*aProperties = mProperties); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::SetProperties(nsIPropertyBag *aProperties) +{ + mProperties = aProperties; + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::ChildAt(PRInt32 index, nsIMetricsEventItem **result) +{ + if (index < 0 || index >= mChildren.Count()) { + return NS_ERROR_INVALID_ARG; + } + + NS_ADDREF(*result = mChildren[index]); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::IndexOf(nsIMetricsEventItem *item, PRInt32 *result) +{ + *result = mChildren.IndexOf(item); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::AppendChild(nsIMetricsEventItem *item) +{ + NS_ENSURE_ARG_POINTER(item); + + NS_ENSURE_TRUE(mChildren.AppendObject(item), NS_ERROR_OUT_OF_MEMORY); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::InsertChildAt(nsIMetricsEventItem *item, PRInt32 index) +{ + NS_ENSURE_ARG_POINTER(item); + if (index < 0 || index > mChildren.Count()) { + return NS_ERROR_INVALID_ARG; + } + + NS_ENSURE_TRUE(mChildren.InsertObjectAt(item, index), + NS_ERROR_OUT_OF_MEMORY); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::RemoveChildAt(PRInt32 index) +{ + if (index < 0 || index >= mChildren.Count()) { + return NS_ERROR_INVALID_ARG; + } + + mChildren.RemoveObjectAt(index); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::ReplaceChildAt(nsIMetricsEventItem *newItem, PRInt32 index) +{ + NS_ENSURE_ARG_POINTER(newItem); + if (index < 0 || index >= mChildren.Count()) { + return NS_ERROR_INVALID_ARG; + } + + mChildren.ReplaceObjectAt(newItem, index); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::ClearChildren() +{ + mChildren.Clear(); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsEventItem::GetChildCount(PRInt32 *childCount) +{ + *childCount = mChildren.Count(); + return NS_OK; +} diff --git a/mozilla/extensions/metrics/src/nsMetricsEventItem.h b/mozilla/extensions/metrics/src/nsMetricsEventItem.h new file mode 100644 index 00000000000..18c71eab033 --- /dev/null +++ b/mozilla/extensions/metrics/src/nsMetricsEventItem.h @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Metrics extension. + * + * The Initial Developer of the Original Code is Google Inc. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsMetricsEventItem_h__ +#define nsMetricsEventItem_h__ + +#include "nsIMetricsService.h" +#include "nsCOMArray.h" +#include "nsCOMPtr.h" + +class nsIPropertyBag; + +// nsMetricsEventItem implements a single event item that can store properties. + +class nsMetricsEventItem : public nsIMetricsEventItem +{ + public: + nsMetricsEventItem(const nsAString &itemNamespace, + const nsAString &itemName); + + NS_DECL_ISUPPORTS + NS_DECL_NSIMETRICSEVENTITEM + + private: + ~nsMetricsEventItem(); + + nsString mNamespace; + nsString mName; + nsCOMPtr mProperties; + nsCOMArray mChildren; +}; + +#endif // nsMetricsEventItem_h__ diff --git a/mozilla/extensions/metrics/src/nsMetricsService.cpp b/mozilla/extensions/metrics/src/nsMetricsService.cpp index 062c64aa870..005ec2c4deb 100644 --- a/mozilla/extensions/metrics/src/nsMetricsService.cpp +++ b/mozilla/extensions/metrics/src/nsMetricsService.cpp @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsMetricsService.h" +#include "nsMetricsEventItem.h" #include "nsXPCOM.h" #include "nsServiceManagerUtils.h" #include "nsDirectoryServiceUtils.h" @@ -113,11 +114,121 @@ NS_IMPL_ISUPPORTS6_CI(nsMetricsService, nsIMetricsService, nsIAboutModule, nsITimerCallback) NS_IMETHODIMP -nsMetricsService::LogEvent(const nsAString &eventNS, - const nsAString &eventName, - nsIPropertyBag *eventProperties) +nsMetricsService::CreateEventItem(const nsAString &itemNamespace, + const nsAString &itemName, + nsIMetricsEventItem **result) { - NS_ENSURE_ARG_POINTER(eventProperties); + *result = nsnull; + + nsMetricsEventItem *item = new nsMetricsEventItem(itemNamespace, itemName); + NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY); + + NS_ADDREF(*result = item); + return NS_OK; +} + +nsresult +nsMetricsService::BuildEventItem(nsIMetricsEventItem *item, + nsIDOMElement **itemElement) +{ + *itemElement = nsnull; + + nsAutoString itemNS, itemName; + item->GetItemNamespace(itemNS); + item->GetItemName(itemName); + + nsCOMPtr element; + nsresult rv = mDocument->CreateElementNS(itemNS, itemName, + getter_AddRefs(element)); + NS_ENSURE_SUCCESS(rv, rv); + + // Attach the given properties as attributes. + nsCOMPtr properties; + item->GetProperties(getter_AddRefs(properties)); + if (properties) { + nsCOMPtr enumerator; + rv = properties->GetEnumerator(getter_AddRefs(enumerator)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr propertySupports; + while (NS_SUCCEEDED( + enumerator->GetNext(getter_AddRefs(propertySupports)))) { + nsCOMPtr property = do_QueryInterface(propertySupports); + if (!property) { + NS_WARNING("PropertyBag enumerator has non-nsIProperty elements"); + continue; + } + + nsAutoString name; + rv = property->GetName(name); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get property name"); + continue; + } + + nsCOMPtr value; + rv = property->GetValue(getter_AddRefs(value)); + if (NS_FAILED(rv) || !value) { + NS_WARNING("Failed to get property value"); + continue; + } + + // If the type is boolean, we want to use the strings "true" and "false", + // rather than "1" and "0" which is what nsVariant generates on its own. + PRUint16 dataType; + value->GetDataType(&dataType); + + nsAutoString valueString; + if (dataType == nsIDataType::VTYPE_BOOL) { + PRBool valueBool; + rv = value->GetAsBool(&valueBool); + if (NS_FAILED(rv)) { + NS_WARNING("Variant has bool type but couldn't get bool value"); + continue; + } + valueString = valueBool ? NS_LITERAL_STRING("true") + : NS_LITERAL_STRING("false"); + } else { + rv = value->GetAsDOMString(valueString); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to convert property value to string"); + continue; + } + } + + rv = element->SetAttribute(name, valueString); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to set attribute value"); + } + continue; + } + } + + // Now recursively build the child event items + PRInt32 childCount = 0; + item->GetChildCount(&childCount); + for (PRInt32 i = 0; i < childCount; ++i) { + nsCOMPtr childItem; + item->ChildAt(i, getter_AddRefs(childItem)); + NS_ASSERTION(childItem, "The child list cannot contain null items"); + + nsCOMPtr childElement; + rv = BuildEventItem(childItem, getter_AddRefs(childElement)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr nodeReturn; + rv = element->AppendChild(childElement, getter_AddRefs(nodeReturn)); + NS_ENSURE_SUCCESS(rv, rv); + } + + element.swap(*itemElement); + return NS_OK; +} + +NS_IMETHODIMP +nsMetricsService::LogEvent(nsIMetricsEventItem *item) +{ + NS_ENSURE_ARG_POINTER(item); if (mSuspendCount != 0) // Ignore events while suspended return NS_OK; @@ -127,72 +238,18 @@ nsMetricsService::LogEvent(const nsAString &eventNS, return NS_OK; // Restrict the types of events logged + nsAutoString eventNS, eventName; + item->GetItemNamespace(eventNS); + item->GetItemName(eventName); + if (!mConfig.IsEventEnabled(eventNS, eventName)) return NS_OK; // Create a DOM element for the event and append it to our document. nsCOMPtr eventElement; - nsresult rv = mDocument->CreateElementNS(eventNS, eventName, - getter_AddRefs(eventElement)); + nsresult rv = BuildEventItem(item, getter_AddRefs(eventElement)); NS_ENSURE_SUCCESS(rv, rv); - // Attach the given properties as attributes. - nsCOMPtr enumerator; - rv = eventProperties->GetEnumerator(getter_AddRefs(enumerator)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr propertySupports; - while (NS_SUCCEEDED(enumerator->GetNext(getter_AddRefs(propertySupports)))) { - nsCOMPtr property = do_QueryInterface(propertySupports); - if (!property) { - NS_WARNING("PropertyBag enumerator has non-nsIProperty elements"); - continue; - } - - nsAutoString name; - rv = property->GetName(name); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to get property name"); - continue; - } - - nsCOMPtr value; - rv = property->GetValue(getter_AddRefs(value)); - if (NS_FAILED(rv) || !value) { - NS_WARNING("Failed to get property value"); - continue; - } - - // If the type is boolean, we want to use the strings "true" and "false", - // rather than "1" and "0" which is what nsVariant generates on its own. - PRUint16 dataType; - value->GetDataType(&dataType); - - nsAutoString valueString; - if (dataType == nsIDataType::VTYPE_BOOL) { - PRBool valueBool; - rv = value->GetAsBool(&valueBool); - if (NS_FAILED(rv)) { - NS_WARNING("Variant has bool type but couldn't get bool value"); - continue; - } - valueString = valueBool ? NS_LITERAL_STRING("true") - : NS_LITERAL_STRING("false"); - } else { - rv = value->GetAsDOMString(valueString); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to convert property value to string"); - continue; - } - } - - rv = eventElement->SetAttribute(name, valueString); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to set attribute value"); - } - continue; - } - // Add the event timestamp nsAutoString timeString; timeString.AppendInt(PR_Now() / PR_USEC_PER_SEC); @@ -213,6 +270,21 @@ nsMetricsService::LogEvent(const nsAString &eventNS, return NS_OK; } +NS_IMETHODIMP +nsMetricsService::LogSimpleEvent(const nsAString &eventNS, + const nsAString &eventName, + nsIPropertyBag *eventProperties) +{ + NS_ENSURE_ARG_POINTER(eventProperties); + + nsCOMPtr item; + nsresult rv = CreateEventItem(eventNS, eventName, getter_AddRefs(item)); + NS_ENSURE_SUCCESS(rv, rv); + + item->SetProperties(eventProperties); + return LogEvent(item); +} + NS_IMETHODIMP nsMetricsService::Flush() { diff --git a/mozilla/extensions/metrics/src/nsMetricsService.h b/mozilla/extensions/metrics/src/nsMetricsService.h index cf141689a46..9067e916bbb 100644 --- a/mozilla/extensions/metrics/src/nsMetricsService.h +++ b/mozilla/extensions/metrics/src/nsMetricsService.h @@ -101,9 +101,9 @@ public: nsresult LogEvent(const nsAString &eventName, nsHashPropertyBag *eventProperties) { - return LogEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), - eventName, - NS_STATIC_CAST(nsIWritablePropertyBag*, eventProperties)); + return LogSimpleEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), eventName, + NS_STATIC_CAST(nsIWritablePropertyBag*, + eventProperties)); } // Get the window id for the given DOMWindow. If a window id has not @@ -150,7 +150,11 @@ private: return mConfig.IsEventEnabled(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), event); } - + + // Builds up a DOMElement tree from the given item and its children + nsresult BuildEventItem(nsIMetricsEventItem *item, + nsIDOMElement **itemElement); + private: // Pointer to the metrics service singleton static nsMetricsService* sMetricsService;