summaryrefslogtreecommitdiff
path: root/chromium/chrome/browser/resources/chromeos/chromevox/walkers/abstract_walker.js
blob: b8d5192daa0c862247874ca6db1e90471be2739b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright 2014 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.

/**
 * @fileoverview An interface (and partial implementation) for the basic
 * traversal through some piece of the dom.
 * For each different ordered (either in dom or by any other metric) set
 * of "valid selections" (just set from now on), a new
 * base class should be defined that implements this interface. For example,
 * there are subclasses for words, sentences, and lowest-level dom nodes.
 * These classes should all be stateless; this makes testing much more
 * effective at pinpointing errors.
 * For all of the operations in this interface, the position in the dom on
 * which to operate is given by a CursorSelection, see that file for
 * documentation.
 * The two main operations that currently exist for walkers are sync and
 * next. See the docs where those functions are defined.
 * Since most operations are hard to even define if there is no root element,
 * all operations may assume that the selection given is attached to the body
 * node. The behavior is undefined if any part of the selection passed in
 * is not attached to the body. As a user of this class, it is your
 * responsibility to make sure the selection is attached.
 * No operation may visibly modify any of its arguments. In particular, take
 * care with CursorSelections, since setReversed modifies the selection.
 * For all documentation, = refers to the method equals for CursorSelections
 * comparison.
 * Thinking of adding something in this class? Here are some good questions to
 * ask:
 * Is this an operation that applies to any element of any arbitrary set?
 * If not, then it probably doesn't belong here.
 * Does it need to know something other than the set that it operates on?
 * If so, then it probably doesn't belong here.
 *
 * This interface resembles a C++ STL bidirectional iterator. Additions should
 * keep this in mind.
 *
 */


goog.provide('cvox.AbstractWalker');

goog.require('cvox.CursorSelection');
goog.require('cvox.NavBraille');

/**
 * @constructor
 */
cvox.AbstractWalker = function() {
};


/**
 * This takes a valid CursorSelection and returns the directed-next
 * valid CursorSelection in the dom, or null. For example, if the walker
 * navigates across sentences, this would return the selection of the sentence
 * following the selection passed in. If sel is at the "end" of a section,
 * this method may return null. In the example above, if we try to next on
 * the last sentence in the dom, we would return null.
 * Note that sel must be a valid selection. Undefined behavior if it isn't.
 * There are several invariants that must hold for any subclasses. There may
 * not be explicit tests for these, but subclasses are responsible for ensuring
 * them and callers may assume them:
 * 1) next(next(sel).setReversed(!sel.isReversed())) = sel for all sel if sel
 *    is a valid CursorSelection and next(sel) != null.
 *  That is, the valid elements for this walker are totally ordered; going
 *  forward and then backward returns us to the same cell.
 * 2) next(sel).isReversed() = sel.isReversed() for all sel if sel is a
 *    valid CursorSelection and next(sel) != null.
 *  That is, next preserves direction.
 * @param {!cvox.CursorSelection} sel The valid selection to start moving from.
 * @return {cvox.CursorSelection} Returns the valid selection the walker moves
 * to. null if directed end of section is reached.
 */
cvox.AbstractWalker.prototype.next = goog.abstractMethod;


/**
 * Syncs and returns the first or last valid, non-null selection in the
 * this walker's linearization of the DOM.
 * @param {{reversed: (undefined|boolean)}=} kwargs Extra arguments.
 *  reversed: If true, syncs to the end and returns a reversed selection.
 *    False by default.
 * @return {!cvox.CursorSelection} The valid selection.
 */
cvox.AbstractWalker.prototype.begin = function(kwargs) {
  kwargs = kwargs || {reversed: false};

  return /** @type {!cvox.CursorSelection} */ (this.sync(
      cvox.CursorSelection.fromBody().setReversed(kwargs.reversed)));
};


/**
 * This takes an arbitrary CursorSelection and returns a valid CursorSelection,
 * or null. For example, if the walker navigates across
 * text nodes, and the selection passed in is for a single character within a
 * larger text node, this method should return a text node. No restrictions
 * are made as to exactly what selection should be returned, but it should be
 * something "reasonable", and from the user's point of view, "close" to the
 * previous selection. If no such selection exists, null may be returned.
 * Note that, since CursorSelection has a direction, syncing to a selection
 * should make sense in either direction.
 * Note also that, as mentioned in the file overview, this operation has
 * undefined behavior if the input selection is not attached to the body.
 * There are several invariants that must hold for any subclasses. While they
 * may not all be tested for at the time, subclasses are responsible for
 * making sure these hold, and any caller may assume these to be true:
 * 1) sync(sel) = sel iff sel is a valid selection
 *    This defines the set of valid selections for this walker.
 *    Note, in particular, that this implies sync(sync(sel)) = sync(sel)
 *    whenever sync(sel) != null.
 * 2) sync(sel).isReversed() = sel.isReversed() for all sel if sync(sel) != null
 *    That is, sync preserves direction.
 * Why do these restrictions exist? Because it makes it much easier to reason
 * about the effect (and intent) of an operation if we can make these
 * assumptions.
 * @param {!cvox.CursorSelection} sel The (possibly unsynched) selection.
 * @return {cvox.CursorSelection} The synched selection.
 */
cvox.AbstractWalker.prototype.sync = goog.abstractMethod;


/**
 * Returns an array of NavDescriptions that defines what should be said
 * by the tts engine on traversal from prevSel to sel. While this is
 * introducing knowledge (of NavDescriptions) into this class that
 * it shouldn't know, this is currently the best place for this method
 * to reside, as the set of valid CursorSelections must be known.
 * sel must be valid CursorSelections for this walker, prevSel may be any
 * selection. Undefined behavior otherwise.
 * @param {!cvox.CursorSelection} prevSel The valid previous selection.
 * @param {!cvox.CursorSelection} sel The valid current selection.
 * @return {!Array.<!cvox.NavDescription>} The description array.
 */
cvox.AbstractWalker.prototype.getDescription = goog.abstractMethod;


/**
 * Returns a NavBraille that defines what should be brailled on traversal from
 * prevSel to sel.
 * sel must be valid CursorSelections for this walker, prevSel may be any
 * selection. Undefined behavior otherwise.
 * @param {!cvox.CursorSelection} prevSel The valid previous selection.
 * @param {!cvox.CursorSelection} sel The valid current selection.
 * @return {!cvox.NavBraille} The braille description.
 */
cvox.AbstractWalker.prototype.getBraille = goog.abstractMethod;


/**
 * Returns if this walker supports the given action.
 * @param {string} name Action name.
 * @return {boolean} True if action supported.
 */
cvox.AbstractWalker.prototype.hasAction = function(name) {
  return typeof(this[name]) == 'function';
};

/**
 * Performs an action specific to the walker.
 * @param {string} name Action name.
 * @param {!cvox.CursorSelection} sel The current selection.
 * @return {cvox.CursorSelection} Selection after action.
 */
cvox.AbstractWalker.prototype.performAction = function(name, sel) {
  if (this.hasAction(name)) {
    return this[name](sel);
  }
  return null;
};

/**
 * Returns message string of the walker's granularity.
 * @return {string} The message string.
 */
cvox.AbstractWalker.prototype.getGranularityMsg = goog.abstractMethod;