| // Copyright 2014 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/service_worker/service_worker_metrics.h" | 
 |  | 
 | #include <limits> | 
 | #include <string> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/metrics/histogram_functions.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/strings/strcat.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/task/post_task.h" | 
 | #include "base/time/time.h" | 
 | #include "content/browser/service_worker/embedded_worker_status.h" | 
 | #include "content/browser/service_worker/service_worker_context_wrapper.h" | 
 | #include "content/public/browser/browser_task_traits.h" | 
 | #include "content/public/browser/browser_thread.h" | 
 | #include "content/public/common/content_client.h" | 
 |  | 
 | namespace content { | 
 |  | 
 | namespace { | 
 |  | 
 | const char* StartSituationToSuffix( | 
 |     ServiceWorkerMetrics::StartSituation situation) { | 
 |   // Don't change these returned strings. They are written (in hashed form) into | 
 |   // logs. | 
 |   switch (situation) { | 
 |     case ServiceWorkerMetrics::StartSituation::UNKNOWN: | 
 |       NOTREACHED(); | 
 |       return ".Unknown"; | 
 |     case ServiceWorkerMetrics::StartSituation::DURING_STARTUP: | 
 |       return ".DuringStartup"; | 
 |     case ServiceWorkerMetrics::StartSituation::NEW_PROCESS: | 
 |       return ".NewProcess"; | 
 |     case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS: | 
 |       return ".ExistingUnreadyProcess"; | 
 |     case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS: | 
 |       return ".ExistingReadyProcess"; | 
 |   } | 
 |   NOTREACHED() << static_cast<int>(situation); | 
 |   return ".Unknown"; | 
 | } | 
 |  | 
 | // TODO(falken): Remove this when the associated UMA are removed. | 
 | const char* StartSituationToDeprecatedSuffix( | 
 |     ServiceWorkerMetrics::StartSituation situation) { | 
 |   // Don't change this returned string. It is written (in hashed form) into | 
 |   // logs. | 
 |   switch (situation) { | 
 |     case ServiceWorkerMetrics::StartSituation::UNKNOWN: | 
 |       NOTREACHED(); | 
 |       return "_Unknown"; | 
 |     case ServiceWorkerMetrics::StartSituation::DURING_STARTUP: | 
 |       return "_DuringStartup"; | 
 |     case ServiceWorkerMetrics::StartSituation::NEW_PROCESS: | 
 |       return "_NewProcess"; | 
 |     case ServiceWorkerMetrics::StartSituation::EXISTING_UNREADY_PROCESS: | 
 |       return "_ExistingUnreadyProcess"; | 
 |     case ServiceWorkerMetrics::StartSituation::EXISTING_READY_PROCESS: | 
 |       return "_ExistingReadyProcess"; | 
 |   } | 
 |   NOTREACHED() << static_cast<int>(situation); | 
 |   return "_Unknown"; | 
 | } | 
 |  | 
 | const char* EventTypeToSuffix(ServiceWorkerMetrics::EventType event_type) { | 
 |   // Don't change these returned strings. They are written (in hashed form) into | 
 |   // logs. | 
 |   switch (event_type) { | 
 |     case ServiceWorkerMetrics::EventType::ACTIVATE: | 
 |       return "_ACTIVATE"; | 
 |     case ServiceWorkerMetrics::EventType::INSTALL: | 
 |       return "_INSTALL"; | 
 |     case ServiceWorkerMetrics::EventType::SYNC: | 
 |       return "_SYNC"; | 
 |     case ServiceWorkerMetrics::EventType::NOTIFICATION_CLICK: | 
 |       return "_NOTIFICATION_CLICK"; | 
 |     case ServiceWorkerMetrics::EventType::PUSH: | 
 |       return "_PUSH"; | 
 |     case ServiceWorkerMetrics::EventType::MESSAGE: | 
 |       return "_MESSAGE"; | 
 |     case ServiceWorkerMetrics::EventType::NOTIFICATION_CLOSE: | 
 |       return "_NOTIFICATION_CLOSE"; | 
 |     case ServiceWorkerMetrics::EventType::FETCH_MAIN_FRAME: | 
 |       return "_FETCH_MAIN_FRAME"; | 
 |     case ServiceWorkerMetrics::EventType::FETCH_SUB_FRAME: | 
 |       return "_FETCH_SUB_FRAME"; | 
 |     case ServiceWorkerMetrics::EventType::FETCH_SHARED_WORKER: | 
 |       return "_FETCH_SHARED_WORKER"; | 
 |     case ServiceWorkerMetrics::EventType::FETCH_SUB_RESOURCE: | 
 |       return "_FETCH_SUB_RESOURCE"; | 
 |     case ServiceWorkerMetrics::EventType::UNKNOWN: | 
 |       return "_UNKNOWN"; | 
 |     case ServiceWorkerMetrics::EventType::FETCH_WAITUNTIL: | 
 |       return "_FETCH_WAITUNTIL"; | 
 |     case ServiceWorkerMetrics::EventType::EXTERNAL_REQUEST: | 
 |       return "_EXTERNAL_REQUEST"; | 
 |     case ServiceWorkerMetrics::EventType::PAYMENT_REQUEST: | 
 |       return "_PAYMENT_REQUEST"; | 
 |     case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_ABORT: | 
 |       return "_BACKGROUND_FETCH_ABORT"; | 
 |     case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_CLICK: | 
 |       return "_BACKGROUND_FETCH_CLICK"; | 
 |     case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_FAIL: | 
 |       return "_BACKGROUND_FETCH_FAIL"; | 
 |     case ServiceWorkerMetrics::EventType::NAVIGATION_HINT: | 
 |       return "_NAVIGATION_HINT"; | 
 |     case ServiceWorkerMetrics::EventType::CAN_MAKE_PAYMENT: | 
 |       return "_CAN_MAKE_PAYMENT"; | 
 |     case ServiceWorkerMetrics::EventType::ABORT_PAYMENT: | 
 |       return "_ABORT_PAYMENT"; | 
 |     case ServiceWorkerMetrics::EventType::COOKIE_CHANGE: | 
 |       return "_COOKIE_CHANGE"; | 
 |     case ServiceWorkerMetrics::EventType::BACKGROUND_FETCH_SUCCESS: | 
 |       return "_BACKGROUND_FETCH_SUCCESS"; | 
 |     case ServiceWorkerMetrics::EventType::PERIODIC_SYNC: | 
 |       return "_PERIODIC_SYNC"; | 
 |     case ServiceWorkerMetrics::EventType::CONTENT_DELETE: | 
 |       return "_CONTENT_DELETE"; | 
 |     case ServiceWorkerMetrics::EventType::PUSH_SUBSCRIPTION_CHANGE: | 
 |       return "_PUSH_SUBSCRIPTION_CHANGE"; | 
 |   } | 
 |   return "_UNKNOWN"; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | const char* ServiceWorkerMetrics::EventTypeToString(EventType event_type) { | 
 |   switch (event_type) { | 
 |     case EventType::ACTIVATE: | 
 |       return "Activate"; | 
 |     case EventType::INSTALL: | 
 |       return "Install"; | 
 |     case EventType::SYNC: | 
 |       return "Sync"; | 
 |     case EventType::NOTIFICATION_CLICK: | 
 |       return "Notification Click"; | 
 |     case EventType::NOTIFICATION_CLOSE: | 
 |       return "Notification Close"; | 
 |     case EventType::PUSH: | 
 |       return "Push"; | 
 |     case EventType::MESSAGE: | 
 |       return "Message"; | 
 |     case EventType::FETCH_MAIN_FRAME: | 
 |       return "Fetch Main Frame"; | 
 |     case EventType::FETCH_SUB_FRAME: | 
 |       return "Fetch Sub Frame"; | 
 |     case EventType::FETCH_SHARED_WORKER: | 
 |       return "Fetch Shared Worker"; | 
 |     case EventType::FETCH_SUB_RESOURCE: | 
 |       return "Fetch Subresource"; | 
 |     case EventType::UNKNOWN: | 
 |       return "Unknown"; | 
 |     case EventType::FETCH_WAITUNTIL: | 
 |       return "Fetch WaitUntil"; | 
 |     case EventType::EXTERNAL_REQUEST: | 
 |       return "External Request"; | 
 |     case EventType::PAYMENT_REQUEST: | 
 |       return "Payment Request"; | 
 |     case EventType::BACKGROUND_FETCH_ABORT: | 
 |       return "Background Fetch Abort"; | 
 |     case EventType::BACKGROUND_FETCH_CLICK: | 
 |       return "Background Fetch Click"; | 
 |     case EventType::BACKGROUND_FETCH_FAIL: | 
 |       return "Background Fetch Fail"; | 
 |     case EventType::NAVIGATION_HINT: | 
 |       return "Navigation Hint"; | 
 |     case EventType::CAN_MAKE_PAYMENT: | 
 |       return "Can Make Payment"; | 
 |     case EventType::ABORT_PAYMENT: | 
 |       return "Abort Payment"; | 
 |     case EventType::COOKIE_CHANGE: | 
 |       return "Cookie Change"; | 
 |     case EventType::BACKGROUND_FETCH_SUCCESS: | 
 |       return "Background Fetch Success"; | 
 |     case EventType::PERIODIC_SYNC: | 
 |       return "Periodic Sync"; | 
 |     case EventType::CONTENT_DELETE: | 
 |       return "Content Delete"; | 
 |     case EventType::PUSH_SUBSCRIPTION_CHANGE: | 
 |       return "Push Subscription Change"; | 
 |   } | 
 |   NOTREACHED() << "Got unexpected event type: " << static_cast<int>(event_type); | 
 |   return "error"; | 
 | } | 
 |  | 
 | const char* ServiceWorkerMetrics::StartSituationToString( | 
 |     StartSituation start_situation) { | 
 |   switch (start_situation) { | 
 |     case StartSituation::UNKNOWN: | 
 |       return "Unknown"; | 
 |     case StartSituation::DURING_STARTUP: | 
 |       return "During startup"; | 
 |     case StartSituation::NEW_PROCESS: | 
 |       return "New process"; | 
 |     case StartSituation::EXISTING_UNREADY_PROCESS: | 
 |       return "Existing unready process"; | 
 |     case StartSituation::EXISTING_READY_PROCESS: | 
 |       return "Existing ready process"; | 
 |   } | 
 |   NOTREACHED() << "Got unexpected start situation: " | 
 |                << static_cast<int>(start_situation); | 
 |   return "error"; | 
 | } | 
 |  | 
 | ServiceWorkerMetrics::Site ServiceWorkerMetrics::SiteFromURL(const GURL& url) { | 
 |   // TODO(falken): Plumb through ContentBrowserClient::GetMetricSuffixForURL or | 
 |   // figure out a way to remove ServiceWorkerMetrics::Site entirely instead of | 
 |   // hardcoding sites in //content. | 
 |  | 
 |   // This inaccurately matches google.example.com, see the TODO above. | 
 |   static const char google_like_scope_prefix[] = "https://www.google."; | 
 |   static const char ntp_scope_path[] = "/_/chrome/"; | 
 |   if (base::StartsWith(url.spec(), google_like_scope_prefix, | 
 |                        base::CompareCase::INSENSITIVE_ASCII) && | 
 |       base::StartsWith(url.path(), ntp_scope_path, | 
 |                        base::CompareCase::SENSITIVE)) { | 
 |     return ServiceWorkerMetrics::Site::NEW_TAB_PAGE; | 
 |   } | 
 |  | 
 |   const base::StringPiece host = url.host_piece(); | 
 |   if (host == "plus.google.com") | 
 |     return ServiceWorkerMetrics::Site::PLUS; | 
 |   if (host == "inbox.google.com") | 
 |     return ServiceWorkerMetrics::Site::INBOX; | 
 |   if (host == "docs.google.com") | 
 |     return ServiceWorkerMetrics::Site::DOCS; | 
 |   if (host == "drive.google.com") { | 
 |     // TODO(falken): This should not be DOCS but historically we logged them | 
 |     // together. | 
 |     return ServiceWorkerMetrics::Site::DOCS; | 
 |   } | 
 |   return ServiceWorkerMetrics::Site::OTHER; | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::CountReadResponseResult( | 
 |     ServiceWorkerMetrics::ReadResponseResult result) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.ReadResponseResult", | 
 |                             result, NUM_READ_RESPONSE_RESULT_TYPES); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::CountWriteResponseResult( | 
 |     ServiceWorkerMetrics::WriteResponseResult result) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.DiskCache.WriteResponseResult", | 
 |                             result, NUM_WRITE_RESPONSE_RESULT_TYPES); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::CountControlledPageLoad(Site site, | 
 |                                                    bool is_main_frame_load) { | 
 |   DCHECK_NE(site, Site::OTHER); | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.PageLoad", site); | 
 |   if (is_main_frame_load) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.MainFramePageLoad", site); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartInstalledWorkerStatus( | 
 |     blink::ServiceWorkerStatusCode status, | 
 |     EventType purpose) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Status", status); | 
 |   base::UmaHistogramEnumeration( | 
 |       base::StrCat({"ServiceWorker.StartWorker.StatusByPurpose", | 
 |                     EventTypeToSuffix(purpose)}), | 
 |       status); | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Purpose", purpose); | 
 |   if (status == blink::ServiceWorkerStatusCode::kErrorTimeout) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.Timeout.StartPurpose", | 
 |                               purpose); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartWorkerTime(base::TimeDelta time, | 
 |                                                  bool is_installed, | 
 |                                                  StartSituation start_situation, | 
 |                                                  EventType purpose) { | 
 |   if (is_installed) { | 
 |     UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartWorker.Time", time); | 
 |     base::UmaHistogramMediumTimes( | 
 |         base::StrCat({"ServiceWorker.StartWorker.Time", | 
 |                       StartSituationToDeprecatedSuffix(start_situation)}), | 
 |         time); | 
 |     base::UmaHistogramMediumTimes( | 
 |         base::StrCat({"ServiceWorker.StartWorker.Time", | 
 |                       StartSituationToDeprecatedSuffix(start_situation), | 
 |                       EventTypeToSuffix(purpose)}), | 
 |         time); | 
 |   } else { | 
 |     UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartNewWorker.Time", time); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordWorkerStopped(StopStatus status) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.WorkerStopped", status); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStopWorkerTime(base::TimeDelta time) { | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StopWorker.Time", time); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordActivateEventStatus( | 
 |     blink::ServiceWorkerStatusCode status, | 
 |     bool is_shutdown) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus", status); | 
 |   if (is_shutdown) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_InShutdown", | 
 |                               status); | 
 |   } else { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.ActivateEventStatus_NotInShutdown", | 
 |                               status); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordInstallEventStatus( | 
 |     blink::ServiceWorkerStatusCode status, | 
 |     uint32_t fetch_count) { | 
 |   base::UmaHistogramEnumeration("ServiceWorker.InstallEvent.All.Status", | 
 |                                 status); | 
 |   base::UmaHistogramCounts1000("ServiceWorker.InstallEvent.All.FetchCount", | 
 |                                fetch_count); | 
 |   if (fetch_count > 0) { | 
 |     base::UmaHistogramEnumeration("ServiceWorker.InstallEvent.WithFetch.Status", | 
 |                                   status); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordEventDuration(EventType event, | 
 |                                                base::TimeDelta time, | 
 |                                                bool was_handled, | 
 |                                                uint32_t fetch_count) { | 
 |   switch (event) { | 
 |     case EventType::ACTIVATE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ActivateEvent.Time", time); | 
 |       break; | 
 |     case EventType::INSTALL: | 
 |       base::UmaHistogramMediumTimes("ServiceWorker.InstallEvent.All.Time", | 
 |                                     time); | 
 |       if (fetch_count) { | 
 |         base::UmaHistogramMediumTimes( | 
 |             "ServiceWorker.InstallEvent.WithFetch.Time", time); | 
 |       } | 
 |       break; | 
 |     case EventType::FETCH_MAIN_FRAME: | 
 |     case EventType::FETCH_SUB_FRAME: | 
 |     case EventType::FETCH_SHARED_WORKER: | 
 |     case EventType::FETCH_SUB_RESOURCE: | 
 |       if (was_handled) { | 
 |         UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.HasResponse.Time", | 
 |                                    time); | 
 |       } else { | 
 |         UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.Fallback.Time", | 
 |                                    time); | 
 |       } | 
 |       break; | 
 |     case EventType::FETCH_WAITUNTIL: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.FetchEvent.WaitUntil.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::SYNC: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundSyncEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::NOTIFICATION_CLICK: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationClickEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::NOTIFICATION_CLOSE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.NotificationCloseEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::PUSH: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PushEvent.Time", time); | 
 |       break; | 
 |     case EventType::MESSAGE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ExtendableMessageEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::EXTERNAL_REQUEST: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ExternalRequest.Time", time); | 
 |       break; | 
 |     case EventType::PAYMENT_REQUEST: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.PaymentRequestEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::BACKGROUND_FETCH_ABORT: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchAbortEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::BACKGROUND_FETCH_CLICK: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchClickEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::BACKGROUND_FETCH_FAIL: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.BackgroundFetchFailEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::BACKGROUND_FETCH_SUCCESS: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |           "ServiceWorker.BackgroundFetchSuccessEvent.Time", time); | 
 |       break; | 
 |     case EventType::CAN_MAKE_PAYMENT: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.CanMakePaymentEvent.Time", | 
 |                                  time); | 
 |       break; | 
 |     case EventType::ABORT_PAYMENT: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.AbortPaymentEvent.Time", time); | 
 |       break; | 
 |     case EventType::COOKIE_CHANGE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.CookieChangeEvent.Time", time); | 
 |       break; | 
 |     case EventType::PERIODIC_SYNC: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |           "ServiceWorker.PeriodicBackgroundSyncEvent.Time", time); | 
 |       break; | 
 |     case EventType::CONTENT_DELETE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.ContentDeleteEvent.Time", time); | 
 |       break; | 
 |     case EventType::PUSH_SUBSCRIPTION_CHANGE: | 
 |       UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |           "ServiceWorker.PushSubscriptionChangeEvent.Time", time); | 
 |       break; | 
 |     case EventType::NAVIGATION_HINT: | 
 |     // The navigation hint should not be sent as an event. | 
 |     case EventType::UNKNOWN: | 
 |       NOTREACHED() << "Invalid event type"; | 
 |       break; | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordFetchEventStatus( | 
 |     bool is_main_resource, | 
 |     blink::ServiceWorkerStatusCode status) { | 
 |   if (is_main_resource) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.MainResource.Status", | 
 |                               status); | 
 |   } else { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.FetchEvent.Subresource.Status", | 
 |                               status); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartWorkerTiming(const StartTimes& times, | 
 |                                                    StartSituation situation) { | 
 |   // Bail if the timings across processes weren't consistent. | 
 |   if (!base::TimeTicks::IsHighResolution() || | 
 |       !base::TimeTicks::IsConsistentAcrossProcesses()) { | 
 |     RecordStartWorkerTimingClockConsistency( | 
 |         CrossProcessTimeDelta::INACCURATE_CLOCK); | 
 |     return; | 
 |   } | 
 |   if (times.remote_start_worker_received < times.local_start_worker_sent || | 
 |       times.local_end < times.remote_script_evaluation_end) { | 
 |     RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NEGATIVE); | 
 |     return; | 
 |   } | 
 |   RecordStartWorkerTimingClockConsistency(CrossProcessTimeDelta::NORMAL); | 
 |  | 
 |   // Total duration. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartTiming.Duration", | 
 |                              times.local_end - times.local_start); | 
 |   base::UmaHistogramMediumTimes( | 
 |       base::StrCat({"ServiceWorker.StartTiming.Duration", | 
 |                     StartSituationToSuffix(situation)}), | 
 |       times.local_end - times.local_start); | 
 |  | 
 |   // SentStartWorker milestone. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES("ServiceWorker.StartTiming.StartToSentStartWorker", | 
 |                              times.local_start_worker_sent - times.local_start); | 
 |  | 
 |   // ReceivedStartWorker milestone. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.StartToReceivedStartWorker", | 
 |       times.remote_start_worker_received - times.local_start); | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.SentStartWorkerToReceivedStartWorker", | 
 |       times.remote_start_worker_received - times.local_start_worker_sent); | 
 |  | 
 |   // ScriptEvaluationStart milestone. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.StartToScriptEvaluationStart", | 
 |       times.remote_script_evaluation_start - times.local_start); | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.ReceivedStartWorkerToScriptEvaluationStart", | 
 |       times.remote_script_evaluation_start - | 
 |           times.remote_start_worker_received); | 
 |  | 
 |   // ScriptEvaluationEnd milestone. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.StartToScriptEvaluationEnd", | 
 |       times.remote_script_evaluation_end - times.local_start); | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.ScriptEvaluationStartToScriptEvaluationEnd", | 
 |       times.remote_script_evaluation_end - | 
 |           times.remote_script_evaluation_start); | 
 |  | 
 |   // End milestone. | 
 |   UMA_HISTOGRAM_MEDIUM_TIMES( | 
 |       "ServiceWorker.StartTiming.ScriptEvaluationEndToEnd", | 
 |       times.local_end - times.remote_script_evaluation_end); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartWorkerTimingClockConsistency( | 
 |     CrossProcessTimeDelta type) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartTiming.ClockConsistency", type); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartStatusAfterFailure( | 
 |     int failure_count, | 
 |     blink::ServiceWorkerStatusCode status) { | 
 |   DCHECK_GT(failure_count, 0); | 
 |  | 
 |   if (status == blink::ServiceWorkerStatusCode::kOk) { | 
 |     UMA_HISTOGRAM_COUNTS_1000("ServiceWorker.StartWorker.FailureStreakEnded", | 
 |                               failure_count); | 
 |   } else if (failure_count < std::numeric_limits<int>::max()) { | 
 |     UMA_HISTOGRAM_COUNTS_1000("ServiceWorker.StartWorker.FailureStreak", | 
 |                               failure_count + 1); | 
 |   } | 
 |  | 
 |   if (failure_count == 1) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_1", | 
 |                               status); | 
 |   } else if (failure_count == 2) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_2", | 
 |                               status); | 
 |   } else if (failure_count == 3) { | 
 |     UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartWorker.AfterFailureStreak_3", | 
 |                               status); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordRuntime(base::TimeDelta time) { | 
 |   // Start at 1 second since we expect service worker to last at least this | 
 |   // long: the update timer and idle timeout timer run on the order of seconds. | 
 |   constexpr base::TimeDelta kMin = base::TimeDelta::FromSeconds(1); | 
 |   // End at 1 day since service workers can conceivably run as long as the the | 
 |   // browser is open; we have to cap somewhere. | 
 |   constexpr base::TimeDelta kMax = base::TimeDelta::FromDays(1); | 
 |   // Set the bucket count to 50 since that is the recommended value for all | 
 |   // histograms. | 
 |   const int kBucketCount = 50; | 
 |  | 
 |   UMA_HISTOGRAM_CUSTOM_TIMES("ServiceWorker.Runtime", time, kMin, kMax, | 
 |                              kBucketCount); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordStartServiceWorkerForNavigationHintResult( | 
 |     StartServiceWorkerForNavigationHintResult result) { | 
 |   UMA_HISTOGRAM_ENUMERATION("ServiceWorker.StartForNavigationHint.Result", | 
 |                             result); | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordLookupRegistrationTime( | 
 |     blink::ServiceWorkerStatusCode status, | 
 |     base::TimeDelta duration) { | 
 |   if (status == blink::ServiceWorkerStatusCode::kOk) { | 
 |     UMA_HISTOGRAM_TIMES( | 
 |         "ServiceWorker.LookupRegistration.MainResource.Time.Exists", duration); | 
 |   } else if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) { | 
 |     UMA_HISTOGRAM_TIMES( | 
 |         "ServiceWorker.LookupRegistration.MainResource.Time.DoesNotExist", | 
 |         duration); | 
 |   } else { | 
 |     UMA_HISTOGRAM_TIMES( | 
 |         "ServiceWorker.LookupRegistration.MainResource.Time.Error", duration); | 
 |   } | 
 | } | 
 |  | 
 | void ServiceWorkerMetrics::RecordOfflineCapableReason( | 
 |     blink::ServiceWorkerStatusCode status, | 
 |     int status_code) { | 
 |   if (status == blink::ServiceWorkerStatusCode::kErrorTimeout) { | 
 |     base::UmaHistogramEnumeration("ServiceWorker.OfflineCapable.Reason", | 
 |                                   OfflineCapableReason::kTimeout); | 
 |     return; | 
 |   } else if (status == blink::ServiceWorkerStatusCode::kOk) { | 
 |     if (200 <= status_code && status_code <= 299) { | 
 |       base::UmaHistogramEnumeration("ServiceWorker.OfflineCapable.Reason", | 
 |                                     OfflineCapableReason::kSuccess); | 
 |       return; | 
 |     } else if (300 <= status_code && status_code <= 399) { | 
 |       base::UmaHistogramEnumeration("ServiceWorker.OfflineCapable.Reason", | 
 |                                     OfflineCapableReason::kRedirect); | 
 |       return; | 
 |     } | 
 |   } | 
 |   NOTREACHED(); | 
 | } | 
 |  | 
 | }  // namespace content |