Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / chrome / browser / resources / chromeos / chromevox / common / memoize.js
blob0ac3f6386ac0e8a1899b94cf61fee76b84d97aef
1 // Copyright 2014 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.
5 /**
6 * @fileoverview Provides a system for memoizing computations applied to
7 * DOM nodes within the same call stack.
9 * To make a function memoizable - suppose you have a function
10 * isAccessible that takes a node and returns a boolean:
12 * function isAccessible(node) {
13 * return expensiveComputation(node);
14 * }
16 * Make it memoizable like this:
18 * function isAccessible(node) {
19 * return cvox.Memoize.memoize(computeIsAccessible_, 'isAccessible', node);
20 * }
22 * function computeIsAccessible_(node) {
23 * return expensiveComputation(node);
24 * }
26 * To take advantage of memoization, you need to wrap a sequence of
27 * computations in a call to memoize.scope() - memoization is only
28 * enabled while in that scope, and all cached data is thrown away at
29 * the end. You should use this only when you're sure the computation
30 * being memoized will not change within the scope.
32 * cvox.Memoize.scope(function() {
33 * console.log(isAccessible(document.body));
34 * });
39 goog.provide('cvox.Memoize');
42 /**
43 * Create the namespace.
44 * @constructor
46 cvox.Memoize = function() {
49 /**
50 * The cache: a map from string function name to a WeakMap from DOM node
51 * to function result. This variable is null when we're out of scope, and it's
52 * a map from string to WeakMap to result when we're in scope.
54 * @type {?Object<WeakMap<Node, *> >}
55 * @private
57 cvox.Memoize.nodeMap_ = null;
59 /**
60 * Keeps track of how many nested times scope() has been called.
61 * @type {number}
62 * @private
64 cvox.Memoize.scopeCount_ = 0;
67 /**
68 * Enables memoization within the scope of the given function. You should
69 * ensure that the DOM is not modified within this scope.
71 * It's safe to nest calls to scope. The nested calls have
72 * no effect, only the outermost one.
74 * @param {Function} functionScope The function to call with memoization
75 * enabled.
76 * @return {*} The value returned by |functionScope|.
78 cvox.Memoize.scope = function(functionScope) {
79 var result;
80 try {
81 cvox.Memoize.scopeCount_++;
82 if (cvox.Memoize.scopeCount_ == 1) {
83 cvox.Memoize.nodeMap_ = {};
85 result = functionScope();
86 } finally {
87 cvox.Memoize.scopeCount_--;
88 if (cvox.Memoize.scopeCount_ == 0) {
89 cvox.Memoize.nodeMap_ = null;
92 return result;
95 /**
96 * Memoizes the result of a function call, so if you call this again
97 * with the same exact parameters and memoization is currently enabled
98 * (via a call to scope()), the second time the cached result
99 * will just be returned directly.
101 * @param {Function} functionClosure The function to call and cache the
102 * result of.
103 * @param {string} functionName The name of the function you're calling.
104 * This string is used to store and retrieve the cached result, so
105 * it should be unique. If the function to be memoized takes simple
106 * arguments in addition to a DOM node, you can incorporate those
107 * arguments into the function name.
108 * @param {Node} node The DOM node that should be passed as the argument
109 * to the function.
110 * @return {*} The return value of |functionClosure|.
112 cvox.Memoize.memoize = function(functionClosure, functionName, node) {
113 if (cvox.Memoize.nodeMap_ &&
114 cvox.Memoize.nodeMap_[functionName] === undefined) {
115 cvox.Memoize.nodeMap_[functionName] = new WeakMap();
118 // If we're not in scope, just call the function directly.
119 if (!cvox.Memoize.nodeMap_) {
120 return functionClosure(node);
123 var result = cvox.Memoize.nodeMap_[functionName].get(node);
124 if (result === undefined) {
125 result = functionClosure(node);
126 if (result === undefined) {
127 throw 'A memoized function cannot return undefined.';
129 cvox.Memoize.nodeMap_[functionName].set(node, result);
132 return result;