/* -*- 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): * Darin Fisher * * 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 nsMetricsService_h__ #define nsMetricsService_h__ #include "nsIMetricsService.h" #include "nsMetricsModule.h" #include "nsMetricsConfig.h" #include "nsIAboutModule.h" #include "nsIStreamListener.h" #include "nsIOutputStream.h" #include "nsIObserver.h" #include "nsITimer.h" #include "nsCOMPtr.h" #include "prio.h" #include "prlog.h" #include "nsIWritablePropertyBag2.h" #include "nsDataHashtable.h" #include "nsInterfaceHashtable.h" #include "nsPtrHashKey.h" #include "blapit.h" class nsILocalFile; class nsIDOMWindow; class nsIDOMDocument; class nsIDOMNode; class nsIMetricsCollector; class nsIDocShellTreeItem; #ifdef PR_LOGGING // Shared log for the metrics service and collectors. // (NSPR_LOG_MODULES=nsMetricsService:5) extern PRLogModuleInfo *gMetricsLog; #endif #define MS_LOG(args) PR_LOG(gMetricsLog, PR_LOG_DEBUG, args) #define MS_LOG_ENABLED() PR_LOG_TEST(gMetricsLog, PR_LOG_DEBUG) // This is the namespace for the built-in metrics events. #define NS_METRICS_NAMESPACE "http://www.mozilla.org/metrics" // This class manages the metrics datastore. It is responsible for persisting // the data when the browser closes and for uploading it to the metrics server // periodically. class nsMetricsService : public nsIMetricsService , public nsIAboutModule , public nsIStreamListener , public nsIObserver , public nsITimerCallback { public: NS_DECL_ISUPPORTS NS_DECL_NSIMETRICSSERVICE NS_DECL_NSIABOUTMODULE NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER NS_DECL_NSIOBSERVER NS_DECL_NSITIMERCALLBACK // Get the metrics service singleton. This method will call do_GetService if // necessary to fetch the metrics service. It relies on the service manager // to keep the singleton instance alive. This method may return null! static nsMetricsService* get(); // Create the metrics service singleton, called only by the XPCOM factory for // this class. static NS_METHOD Create(nsISupports *outer, const nsIID &iid, void **result); // Helper function for logging events in the default namespace nsresult LogEvent(const nsAString &eventName, nsIWritablePropertyBag2 *eventProperties) { nsCOMPtr bag = do_QueryInterface(eventProperties); NS_ENSURE_STATE(bag); return LogSimpleEvent(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), eventName, bag); } // Creates an EventItem in the default metrics namespace. nsresult CreateEventItem(const nsAString &name, nsIMetricsEventItem **item) { return CreateEventItem(NS_LITERAL_STRING(NS_METRICS_NAMESPACE), name, item); } // More convenient non-scriptable version of GetWindowID(). static PRUint32 GetWindowID(nsIDOMWindow *window); // VC6 needs this to be public :-( nsresult Init(); // Returns the window id map (readonly) const nsDataHashtable< nsPtrHashKey, PRUint32 >& WindowMap() const { return mWindowMap; } // Creates a one-way hash of the given string. nsresult HashUTF8(const nsCString &str, nsCString &hashed); // Convenience method for hashing UTF-16 strings. // The string is converted to UTF-8, then HashUTF8() is called. nsresult HashUTF16(const nsString &str, nsCString &hashed) { return HashUTF8(NS_ConvertUTF16toUTF8(str), hashed); } private: nsMetricsService(); ~nsMetricsService(); // Post-profile-initialization startup code nsresult ProfileStartup(); // Reads the config, starts a new session, and turns on collectors nsresult StartCollection(); // Stops collectors and removes all metrics-related prefs and files nsresult StopCollection(); // Starts and stops collectors based on the current configuration void EnableCollectors(); // Creates a new root element to hold event nodes nsresult CreateRoot(); nsresult UploadData(); nsresult GetDataFile(nsCOMPtr *result); nsresult OpenDataFile(PRUint32 flags, PRFileDesc **result); nsresult GetDataFileForUpload(nsCOMPtr *result); // This method returns an input stream containing the complete XML for the // data to upload. nsresult OpenCompleteXMLStream(nsILocalFile *dataFile, nsIInputStream **result); // Initialize our timer for the next upload. // If immediate is true, the timer will be fired as soon as possible, // which is at the deferred-upload time if one exists, or immediately // if not. void InitUploadTimer(PRBool immediate); // A reference to the local file containing our current configuration void GetConfigFile(nsIFile **result); // A reference to the local file where we'll download the server response. // We don't replace the real config file until we know the new one is valid. void GetConfigTempFile(nsIFile **result); // Generate a new random client id string nsresult GenerateClientID(nsCString &clientID); // Check if a built-in event is enabled PRBool IsEventEnabled(const nsAString &event) const { 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); // Called to persist mEventCount. Returns "true" if succeeded. PRBool PersistEventCount(); // Hashes a byte buffer of the given length nsresult HashBytes(const PRUint8 *bytes, PRUint32 length, nsACString &result); // Does the real work of GetWindowID(). PRUint32 GetWindowIDInternal(nsIDOMWindow *window); // Tries to load a new config. If successful, the old config file is // replaced with the new one. If the new config couldn't be loaded, // a config file is written which disables collection and preserves the // upload interval from the old config. Returns true if the new config // file was loaded successfully. PRBool LoadNewConfig(nsIFile *newConfig, nsIFile *oldConfig); // Removes the existing data file (metrics.xml) void RemoveDataFile(); // Generates a random interval, in seconds, between 12 and 36 hours. PRInt32 GetRandomUploadInterval(); static PLDHashOperator PR_CALLBACK PruneDisabledCollectors(const nsAString &key, nsCOMPtr &value, void *userData); static PLDHashOperator PR_CALLBACK DetachCollector(const nsAString &key, nsIMetricsCollector *value, void *userData); static PLDHashOperator PR_CALLBACK NotifyNewLog(const nsAString &key, nsIMetricsCollector *value, void *userData); // Helpers to set a pref and then flush the pref file to disk. static nsresult FlushIntPref(const char *prefName, PRInt32 prefValue); static nsresult FlushCharPref(const char *prefName, const char *prefValue); static nsresult FlushClearPref(const char *prefName); // Returns true if the pref to enable collection is set to true static PRBool CollectionEnabled(); private: class BadCertListener; // Pointer to the metrics service singleton static nsMetricsService* sMetricsService; nsMetricsConfig mConfig; // This output stream is non-null when we are downloading the config file. nsCOMPtr mConfigOutputStream; // XML document containing events to be flushed. nsCOMPtr mDocument; // Root element of the XML document nsCOMPtr mRoot; // MD5 hashing object for collectors to use MD5Context *mMD5Context; // Window to incrementing-id map. The keys are nsIDOMWindow*. nsDataHashtable< nsPtrHashKey, PRUint32 > mWindowMap; // All of the active observers, keyed by name. nsInterfaceHashtable mCollectorMap; // Timer object used for uploads nsCOMPtr mUploadTimer; // The max number of times we'll retry the upload before stopping // for several hours. static const PRUint32 kMaxRetries; // This is the logging format version that is sent with each upload. // The version should be incremented whenver a change is made that // affects the log output, including but not limited to: // - adding or removing a collector // - adding or removing a property // - changing the meaning or interpretation of a property value static const PRUint32 kMetricsVersion; PRInt32 mEventCount; PRInt32 mSuspendCount; PRBool mUploading; nsString mSessionID; // the next window id to hand out PRUint32 mNextWindowID; // The number of times we've tried to upload PRUint32 mRetryCount; }; class nsMetricsUtils { public: // Creates a new nsIWritablePropertyBag2 instance. static nsresult NewPropertyBag(nsIWritablePropertyBag2 **result); // Creates a new item with the given properties, and appends it to the parent static nsresult AddChildItem(nsIMetricsEventItem *parent, const nsAString &childName, nsIPropertyBag *childProperties); // Loops until the given number of random bytes have been returned // from the OS. Returns true on success, or false if no random // bytes are available static PRBool GetRandomNoise(void *buf, PRSize size); // Creates a new element in the metrics namespace, using the given // ownerDocument and tag. static nsresult CreateElement(nsIDOMDocument *ownerDoc, const nsAString &tag, nsIDOMElement **element); // Returns true if the docshell should be considered a subframe. static PRBool IsSubframe(nsIDocShellTreeItem *docShell); // Finds the window id for the DOMWindow containing |node|. // Returns 0 if the window could not be found. static PRUint32 FindWindowForNode(nsIDOMNode *node); }; #endif // nsMetricsService_h__