1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5var EventsTracker = (function() {
6  'use strict';
7
8  /**
9   * This class keeps track of all NetLog events.
10   * It receives events from the browser and when loading a log file, and passes
11   * them on to all its observers.
12   *
13   * @constructor
14   */
15  function EventsTracker() {
16    assertFirstConstructorCall(EventsTracker);
17
18    this.capturedEvents_ = [];
19    this.observers_ = [];
20
21    // Controls how large |capturedEvents_| can grow.
22    this.softLimit_ = Infinity;
23    this.hardLimit_ = Infinity;
24  }
25
26  cr.addSingletonGetter(EventsTracker);
27
28  EventsTracker.prototype = {
29    /**
30     * Returns a list of all captured events.
31     */
32    getAllCapturedEvents: function() {
33      return this.capturedEvents_;
34    },
35
36    /**
37     * Returns the number of events that were captured.
38     */
39    getNumCapturedEvents: function() {
40      return this.capturedEvents_.length;
41    },
42
43    /**
44     * Deletes all the tracked events, and notifies any observers.
45     */
46    deleteAllLogEntries: function() {
47      this.capturedEvents_ = [];
48      for (var i = 0; i < this.observers_.length; ++i)
49        this.observers_[i].onAllLogEntriesDeleted();
50    },
51
52    /**
53     * Adds captured events, and broadcasts them to any observers.
54     */
55    addLogEntries: function(logEntries) {
56      // When reloading a page, it's possible to receive events before
57      // Constants.  Discard those events, as they can cause the fake
58      // "REQUEST_ALIVE" events for pre-existing requests not be the first
59      // events for those requests.
60      if (Constants == null)
61        return;
62
63      this.capturedEvents_ = this.capturedEvents_.concat(logEntries);
64      for (var i = 0; i < this.observers_.length; ++i) {
65        this.observers_[i].onReceivedLogEntries(logEntries);
66      }
67
68      // Check that we haven't grown too big. If so, toss out older events.
69      if (this.getNumCapturedEvents() > this.hardLimit_) {
70        var originalEvents = this.capturedEvents_;
71        this.deleteAllLogEntries();
72        // Delete the oldest events until we reach the soft limit.
73        originalEvents.splice(0, originalEvents.length - this.softLimit_);
74        this.addLogEntries(originalEvents);
75      }
76    },
77
78    /**
79     * Adds a listener of log entries. |observer| will be called back when new
80     * log data arrives or all entries are deleted:
81     *
82     *   observer.onReceivedLogEntries(entries)
83     *   observer.onAllLogEntriesDeleted()
84     */
85    addLogEntryObserver: function(observer) {
86      this.observers_.push(observer);
87    },
88
89    /**
90     * Set bounds on the maximum number of events that will be tracked. This
91     * helps to bound the total amount of memory usage, since otherwise
92     * long-running capture sessions can exhaust the renderer's memory and
93     * crash.
94     *
95     * Once |hardLimit| number of events have been captured we do a garbage
96     * collection and toss out old events, bringing our count down to
97     * |softLimit|.
98     *
99     * To log observers this will look like all the events got deleted, and
100     * then subsequently a bunch of new events were received. In other words, it
101     * behaves the same as if the user had simply started logging a bit later
102     * in time!
103     */
104    setLimits: function(softLimit, hardLimit) {
105      if (hardLimit != Infinity && softLimit >= hardLimit)
106        throw 'hardLimit must be greater than softLimit';
107
108      this.softLimit_ = softLimit;
109      this.hardLimit_ = hardLimit;
110    }
111  };
112
113  return EventsTracker;
114})();
115