browser/devtools/profiler/cleopatra/js/ProgressReporter.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rwxr-xr-x

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /**
michael@0 6 * ProgressReporter
michael@0 7 *
michael@0 8 * This class is used by long-winded tasks to report progress to observers.
michael@0 9 * If a task has subtasks that want to report their own progress, these
michael@0 10 * subtasks can have their own progress reporters which are hooked up to the
michael@0 11 * parent progress reporter, resulting in a tree structure. A parent progress
michael@0 12 * reporter will calculate its progress value as a weighted sum of its
michael@0 13 * subreporters' progress values.
michael@0 14 *
michael@0 15 * A progress reporter has a state, an action, and a progress value.
michael@0 16 *
michael@0 17 * - state is one of STATE_WAITING, STATE_DOING and STATE_FINISHED.
michael@0 18 * - action is a string that describes the current task.
michael@0 19 * - progress is the progress value as a number between 0 and 1, or NaN if
michael@0 20 * indeterminate.
michael@0 21 *
michael@0 22 * A progress reporter starts out in the WAITING state. The DOING state is
michael@0 23 * entered with the begin method which also sets the action. While the task is
michael@0 24 * executing, the progress value can be updated with the setProgress method.
michael@0 25 * When a task has finished, it can call the finish method which is just a
michael@0 26 * shorthand for setProgress(1); this will set the state to FINISHED.
michael@0 27 *
michael@0 28 * Progress observers can be added with the addListener method which takes a
michael@0 29 * function callback. Whenever the progress value or state change, all
michael@0 30 * listener callbacks will be called with the progress reporter object. The
michael@0 31 * observer can get state, progress value and action by calling the getter
michael@0 32 * methods getState(), getProgress() and getAction().
michael@0 33 *
michael@0 34 * Creating child progress reporters for subtasks can be done with the
michael@0 35 * addSubreporter(s) methods. If a progress reporter has subreporters, normal
michael@0 36 * progress report functions (setProgress and finish) can no longer be called.
michael@0 37 * Instead, the parent reporter will listen to progress changes on its
michael@0 38 * subreporters and update its state automatically, and then notify its own
michael@0 39 * listeners.
michael@0 40 * When adding a subreporter, you are expected to provide an estimated
michael@0 41 * duration for the subtask. This value will be used as a weight when
michael@0 42 * calculating the progress of the parent reporter.
michael@0 43 */
michael@0 44
michael@0 45 "use strict";
michael@0 46
michael@0 47 const gDebugExpectedDurations = false;
michael@0 48
michael@0 49 function ProgressReporter() {
michael@0 50 this._observers = [];
michael@0 51 this._subreporters = [];
michael@0 52 this._subreporterExpectedDurationsSum = 0;
michael@0 53 this._progress = 0;
michael@0 54 this._state = ProgressReporter.STATE_WAITING;
michael@0 55 this._action = "";
michael@0 56 }
michael@0 57
michael@0 58 ProgressReporter.STATE_WAITING = 0;
michael@0 59 ProgressReporter.STATE_DOING = 1;
michael@0 60 ProgressReporter.STATE_FINISHED = 2;
michael@0 61
michael@0 62 ProgressReporter.prototype = {
michael@0 63 getProgress: function () {
michael@0 64 return this._progress;
michael@0 65 },
michael@0 66 getState: function () {
michael@0 67 return this._state;
michael@0 68 },
michael@0 69 setAction: function (action) {
michael@0 70 this._action = action;
michael@0 71 this._reportProgress();
michael@0 72 },
michael@0 73 getAction: function () {
michael@0 74 switch (this._state) {
michael@0 75 case ProgressReporter.STATE_WAITING:
michael@0 76 return "Waiting for preceding tasks to finish...";
michael@0 77 case ProgressReporter.STATE_DOING:
michael@0 78 return this._action;
michael@0 79 case ProgressReporter.STATE_FINISHED:
michael@0 80 return "Finished.";
michael@0 81 default:
michael@0 82 throw "Broken state";
michael@0 83 }
michael@0 84 },
michael@0 85 addListener: function (callback) {
michael@0 86 this._observers.push(callback);
michael@0 87 },
michael@0 88 addSubreporter: function (expectedDuration) {
michael@0 89 this._subreporterExpectedDurationsSum += expectedDuration;
michael@0 90 var subreporter = new ProgressReporter();
michael@0 91 var self = this;
michael@0 92 subreporter.addListener(function (progress) {
michael@0 93 self._recalculateProgressFromSubreporters();
michael@0 94 self._recalculateStateAndActionFromSubreporters();
michael@0 95 self._reportProgress();
michael@0 96 });
michael@0 97 this._subreporters.push({ expectedDuration: expectedDuration, reporter: subreporter });
michael@0 98 return subreporter;
michael@0 99 },
michael@0 100 addSubreporters: function (expectedDurations) {
michael@0 101 var reporters = {};
michael@0 102 for (var key in expectedDurations) {
michael@0 103 reporters[key] = this.addSubreporter(expectedDurations[key]);
michael@0 104 }
michael@0 105 return reporters;
michael@0 106 },
michael@0 107 begin: function (action) {
michael@0 108 this._startTime = Date.now();
michael@0 109 this._state = ProgressReporter.STATE_DOING;
michael@0 110 this._action = action;
michael@0 111 this._reportProgress();
michael@0 112 },
michael@0 113 setProgress: function (progress) {
michael@0 114 if (this._subreporters.length > 0)
michael@0 115 throw "Can't call setProgress on a progress reporter with subreporters";
michael@0 116 if (progress != this._progress &&
michael@0 117 (progress == 1 ||
michael@0 118 (isNaN(progress) != isNaN(this._progress)) ||
michael@0 119 (progress - this._progress >= 0.01))) {
michael@0 120 this._progress = progress;
michael@0 121 if (progress == 1)
michael@0 122 this._transitionToFinished();
michael@0 123 this._reportProgress();
michael@0 124 }
michael@0 125 },
michael@0 126 finish: function () {
michael@0 127 this.setProgress(1);
michael@0 128 },
michael@0 129 _recalculateProgressFromSubreporters: function () {
michael@0 130 if (this._subreporters.length == 0)
michael@0 131 throw "Can't _recalculateProgressFromSubreporters on a progress reporter without any subreporters";
michael@0 132 this._progress = 0;
michael@0 133 for (var i = 0; i < this._subreporters.length; i++) {
michael@0 134 var expectedDuration = this._subreporters[i].expectedDuration;
michael@0 135 var reporter = this._subreporters[i].reporter;
michael@0 136 this._progress += reporter.getProgress() * expectedDuration / this._subreporterExpectedDurationsSum;
michael@0 137 }
michael@0 138 },
michael@0 139 _recalculateStateAndActionFromSubreporters: function () {
michael@0 140 if (this._subreporters.length == 0)
michael@0 141 throw "Can't _recalculateStateAndActionFromSubreporters on a progress reporter without any subreporters";
michael@0 142 var actions = [];
michael@0 143 var allWaiting = true;
michael@0 144 var allFinished = true;
michael@0 145 for (var i = 0; i < this._subreporters.length; i++) {
michael@0 146 var expectedDuration = this._subreporters[i].expectedDuration;
michael@0 147 var reporter = this._subreporters[i].reporter;
michael@0 148 var state = reporter.getState();
michael@0 149 if (state != ProgressReporter.STATE_WAITING)
michael@0 150 allWaiting = false;
michael@0 151 if (state != ProgressReporter.STATE_FINISHED)
michael@0 152 allFinished = false;
michael@0 153 if (state == ProgressReporter.STATE_DOING)
michael@0 154 actions.push(reporter.getAction());
michael@0 155 }
michael@0 156 if (allFinished) {
michael@0 157 this._transitionToFinished();
michael@0 158 } else if (!allWaiting) {
michael@0 159 this._state = ProgressReporter.STATE_DOING;
michael@0 160 if (actions.length == 0) {
michael@0 161 this._action = "About to start next task..."
michael@0 162 } else {
michael@0 163 this._action = actions.join("\n");
michael@0 164 }
michael@0 165 }
michael@0 166 },
michael@0 167 _transitionToFinished: function () {
michael@0 168 this._state = ProgressReporter.STATE_FINISHED;
michael@0 169
michael@0 170 if (gDebugExpectedDurations) {
michael@0 171 this._realDuration = Date.now() - this._startTime;
michael@0 172 if (this._subreporters.length) {
michael@0 173 for (var i = 0; i < this._subreporters.length; i++) {
michael@0 174 var expectedDuration = this._subreporters[i].expectedDuration;
michael@0 175 var reporter = this._subreporters[i].reporter;
michael@0 176 var realDuration = reporter._realDuration;
michael@0 177 dump("For reporter with expectedDuration " + expectedDuration + ", real duration was " + realDuration + "\n");
michael@0 178 }
michael@0 179 }
michael@0 180 }
michael@0 181 },
michael@0 182 _reportProgress: function () {
michael@0 183 for (var i = 0; i < this._observers.length; i++) {
michael@0 184 this._observers[i](this);
michael@0 185 }
michael@0 186 },
michael@0 187 };

mercurial