Merge Chromium + Blink git repositories
[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.
8  *
9  * To make a function memoizable - suppose you have a function
10  * isAccessible that takes a node and returns a boolean:
11  *
12  * function isAccessible(node) {
13  *   return expensiveComputation(node);
14  * }
15  *
16  * Make it memoizable like this:
17  *
18  * function isAccessible(node) {
19  *   return cvox.Memoize.memoize(computeIsAccessible_, 'isAccessible', node);
20  * }
21  *
22  * function computeIsAccessible_(node) {
23  *   return expensiveComputation(node);
24  * }
25  *
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.
31  *
32  * cvox.Memoize.scope(function() {
33  *   console.log(isAccessible(document.body));
34  * });
35  *
36  */
39 goog.provide('cvox.Memoize');
42 /**
43  * Create the namespace.
44  * @constructor
45  */
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.
53  *
54  * @type {?Object<WeakMap<Node, *> >}
55  * @private
56  */
57 cvox.Memoize.nodeMap_ = null;
59 /**
60  * Keeps track of how many nested times scope() has been called.
61  * @type {number}
62  * @private
63  */
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.
70  *
71  * It's safe to nest calls to scope. The nested calls have
72  * no effect, only the outermost one.
73  *
74  * @param {Function} functionScope The function to call with memoization
75  *     enabled.
76  * @return {*} The value returned by |functionScope|.
77  */
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_ = {};
84     }
85     result = functionScope();
86   } finally {
87     cvox.Memoize.scopeCount_--;
88     if (cvox.Memoize.scopeCount_ == 0) {
89       cvox.Memoize.nodeMap_ = null;
90     }
91   }
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|.
111  */
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();
116   }
118   // If we're not in scope, just call the function directly.
119   if (!cvox.Memoize.nodeMap_) {
120     return functionClosure(node);
121   }
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.';
128     }
129     cvox.Memoize.nodeMap_[functionName].set(node, result);
130   }
132   return result;