Index: chrome/browser/resources/google_now/utility.js |
diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..9ef1f4a45e5290cf5afbf48b143278469235e811 |
--- /dev/null |
+++ b/chrome/browser/resources/google_now/utility.js |
@@ -0,0 +1,156 @@ |
+// Copyright (c) 2013 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. |
+ |
+'use strict'; |
+ |
+/** |
+ * @fileoverview Utility objects and functions for Google Now extension. |
+ */ |
+ |
+/** |
+ * Checks for internal errors. |
+ * @param {boolean} condition Condition that must be true. |
+ * @param {string} message Diagnostic message for the case when the condition is |
+ * false. |
+ */ |
+function verify(condition, message) { |
+ // TODO(vadimt): Send UMAs instead of showing alert. |
+ // TODO(vadimt): Make sure the execution doesn't continue after this call. |
+ if (!condition) { |
+ var errorText = 'ASSERT: ' + message; |
+ console.error(errorText); |
+ alert(errorText); |
+ } |
+} |
+ |
+/** |
+ * Builds the object to manage tasks (mutually exclusive chains of events). |
+ * @param {function(string, string): boolean} areConflicting Function that |
+ * checks if a new task can't be added to a task queue that contains an |
+ * existing task. |
+ * @return {Object} Task manager interface. |
+ */ |
+function buildTaskManager(areConflicting) { |
+ /** |
+ * Name of the alarm that triggers the error saying that the event page cannot |
+ * unload. |
+ */ |
+ var CANNOT_UNLOAD_ALARM_NAME = 'CANNOT-UNLOAD'; |
+ |
+ /** |
+ * Maximal time we expect the event page to stay loaded after starting a task. |
+ */ |
+ var MAXIMUM_LOADED_TIME_MINUTES = 5; |
+ |
+ /** |
+ * Queue of scheduled tasks. The first element, if present, corresponds to the |
+ * currently running task. |
+ * @type {Array.<Object.<string, function(function())>>} |
+ */ |
+ var queue = []; |
+ |
+ /** |
+ * Name of the current step of the currently running task if present, |
+ * otherwise, null. For diagnostics only. |
+ * It's set when the task is started and before each asynchronous operation. |
+ */ |
+ var stepName = null; |
+ |
+ /** |
+ * Starts the first queued task. |
+ */ |
+ function startFirst() { |
+ verify(queue.length >= 1, 'startFirst: queue is empty'); |
+ |
+ // Set alarm to verify that the event page will unload in a reasonable time. |
+ chrome.alarms.create(CANNOT_UNLOAD_ALARM_NAME, |
+ {delayInMinutes: MAXIMUM_LOADED_TIME_MINUTES}); |
+ |
+ // Start the oldest queued task, but don't remove it from the queue. |
+ verify(stepName == null, 'tasks.startFirst: stepName is not null'); |
+ var entry = queue[0]; |
+ stepName = entry.name + '-initial'; |
+ entry.task(finish); |
+ } |
+ |
+ /** |
+ * Checks if a new task can be added to the task queue. |
+ * @param {string} taskName Name of the new task. |
+ * @return {boolean} Whether the new task can be added. |
+ */ |
+ function canQueue(taskName) { |
+ for (var i = 0; i < queue.length; ++i) { |
+ if (areConflicting(taskName, queue[i].name)) |
+ return false; |
+ } |
+ |
+ return true; |
+ } |
+ |
+ /** |
+ * Adds a new task. If another task is not running, runs the task immediately. |
+ * If any task in the queue is not compatible with the task, ignores the new |
+ * task. Otherwise, stores the task for future execution. |
+ * @param {string} taskName Name of the task. |
+ * @param {function(function())} task Function to run. Takes a callback |
+ * parameter. |
+ */ |
+ function add(taskName, task) { |
+ if (!canQueue(taskName)) |
+ return; |
+ |
+ queue.push({name: taskName, task: task}); |
+ |
+ if (queue.length == 1) { |
+ startFirst(); |
+ } |
+ } |
+ |
+ /** |
+ * Completes the current task and starts the next queued task if available. |
+ */ |
+ function finish() { |
+ verify(queue.length >= 1, 'tasks.finish: The task queue is empty.'); |
+ queue.shift(); |
+ stepName = null; |
+ |
+ if (queue.length >= 1) |
+ startFirst(); |
+ } |
+ |
+ /** |
+ * Associates a name with the current step of the task. Used for diagnostics |
+ * only. A task is a chain of asynchronous events; debugSetStepName should be |
+ * called before starting any asynchronous operation. |
+ * @param {string} step Name of new step. |
+ */ |
+ function debugSetStepName(step) { |
+ // TODO(vadimt): Pass UMA counters instead of step names. |
+ stepName = step; |
+ } |
+ |
+ chrome.alarms.onAlarm.addListener(function(alarm) { |
+ if (alarm.name == CANNOT_UNLOAD_ALARM_NAME) { |
+ // Error if the event page wasn't unloaded after a reasonable timeout |
+ // since starting the last task. |
+ // TODO(vadimt): Uncomment the verify once this bug is fixed: |
+ // crbug.com/177563 |
+ // verify(false, 'Event page didn\'t unload, queue = ' + |
+ // JSON.stringify(tasks) + ', step = ' + stepName + ' (ignore this verify |
+ // if devtools is attached).'); |
+ } |
+ }); |
+ |
+ chrome.runtime.onSuspend.addListener(function() { |
+ chrome.alarms.clear(CANNOT_UNLOAD_ALARM_NAME); |
+ verify(queue.length == 0 && stepName == null, |
+ 'Incomplete task when unloading event page, queue = ' + |
+ JSON.stringify(queue) + ', step = ' + stepName); |
+ }); |
+ |
+ return { |
+ add: add, |
+ debugSetStepName: debugSetStepName |
+ }; |
+} |