Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(460)

Unified Diff: chrome/browser/resources/google_now/utility.js

Issue 12316075: Preventing race conditions in Google Now extension (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: arv@ comments. Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/browser/resources/google_now/manifest.json ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
+ };
+}
« no previous file with comments | « chrome/browser/resources/google_now/manifest.json ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698