| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview A drop-down menu in the ChromeVox panel. | 6 * @fileoverview A drop-down menu in the ChromeVox panel. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 goog.provide('PanelMenu'); | 9 goog.provide('PanelMenu'); |
| 10 goog.provide('PanelNodeMenu'); | 10 goog.provide('PanelNodeMenu'); |
| 11 | 11 |
| 12 goog.require('AutomationTreeWalker'); |
| 12 goog.require('Output'); | 13 goog.require('Output'); |
| 13 goog.require('PanelMenuItem'); | 14 goog.require('PanelMenuItem'); |
| 14 goog.require('constants'); | 15 goog.require('constants'); |
| 15 goog.require('cursors.Range'); | 16 goog.require('cursors.Range'); |
| 16 | 17 |
| 17 /** | 18 /** |
| 18 * @param {string} menuMsg The msg id of the menu. | 19 * @param {string} menuMsg The msg id of the menu. |
| 19 * @constructor | 20 * @constructor |
| 20 */ | 21 */ |
| 21 PanelMenu = function(menuMsg) { | 22 PanelMenu = function(menuMsg) { |
| (...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 break; | 223 break; |
| 223 } | 224 } |
| 224 } | 225 } |
| 225 } | 226 } |
| 226 }; | 227 }; |
| 227 | 228 |
| 228 /** | 229 /** |
| 229 * @param {string} menuMsg The msg id of the menu. | 230 * @param {string} menuMsg The msg id of the menu. |
| 230 * @param {chrome.automation.AutomationNode} node ChromeVox's current position. | 231 * @param {chrome.automation.AutomationNode} node ChromeVox's current position. |
| 231 * @param {AutomationPredicate.Unary} pred Filter to use on the document. | 232 * @param {AutomationPredicate.Unary} pred Filter to use on the document. |
| 233 * @param {boolean} async If true, populates the menu asynchronously by |
| 234 * posting a task after searching each chunk of nodes. |
| 232 * @extends {PanelMenu} | 235 * @extends {PanelMenu} |
| 233 * @constructor | 236 * @constructor |
| 234 */ | 237 */ |
| 235 PanelNodeMenu = function(menuMsg, node, pred) { | 238 PanelNodeMenu = function(menuMsg, node, pred, async) { |
| 236 PanelMenu.call(this, menuMsg); | 239 PanelMenu.call(this, menuMsg); |
| 237 var nodes = []; | 240 this.node_ = node; |
| 238 var selectNext = false; | 241 this.pred_ = pred; |
| 239 var activeIndex = -1; | 242 this.async_ = async; |
| 240 AutomationUtil.findNodePre(node.root, constants.Dir.FORWARD, | 243 this.populate_(); |
| 241 /** @type {AutomationPredicate.Unary} */(function(n) { | 244 }; |
| 242 if (n === node) | |
| 243 selectNext = true; | |
| 244 | 245 |
| 245 if (pred(n)) { | 246 /** |
| 246 var output = new Output(); | 247 * The number of nodes to search before posting a task to finish |
| 247 var range = cursors.Range.fromNode(n); | 248 * searching. |
| 248 output.withSpeech(range, range, Output.EventType.NAVIGATE); | 249 * @const {number} |
| 249 var label = output.toString(); | 250 */ |
| 250 this.addMenuItem(label, '', function() { | 251 PanelNodeMenu.MAX_NODES_BEFORE_ASYNC = 100; |
| 251 chrome.extension.getBackgroundPage().ChromeVoxState | |
| 252 .instance['navigateToRange'](cursors.Range.fromNode(n)); | |
| 253 }); | |
| 254 if (selectNext) { | |
| 255 activeIndex = this.items_.length - 1; | |
| 256 selectNext = false; | |
| 257 } | |
| 258 } | |
| 259 }).bind(this) | |
| 260 ); | |
| 261 | |
| 262 if (!this.items_.length) { | |
| 263 this.addMenuItem( | |
| 264 Msgs.getMsg('panel_menu_item_none'), '', function() {}); | |
| 265 this.activateItem(0); | |
| 266 } | |
| 267 if (activeIndex >= 0) | |
| 268 this.activateItem(activeIndex); | |
| 269 }; | |
| 270 | 252 |
| 271 PanelNodeMenu.prototype = { | 253 PanelNodeMenu.prototype = { |
| 272 __proto__: PanelMenu.prototype, | 254 __proto__: PanelMenu.prototype, |
| 273 | 255 |
| 274 /** @override */ | 256 /** @override */ |
| 275 activate: function() { | 257 activate: function() { |
| 276 var activeItem = this.activeIndex_; | 258 var activeItem = this.activeIndex_; |
| 277 PanelMenu.prototype.activate.call(this); | 259 PanelMenu.prototype.activate.call(this); |
| 278 this.activateItem(activeItem); | 260 this.activateItem(activeItem); |
| 261 }, |
| 262 |
| 263 /** |
| 264 * Create the AutomationTreeWalker and kick off the search to find |
| 265 * nodes that match the predicate for this menu. |
| 266 * @private |
| 267 */ |
| 268 populate_: function() { |
| 269 if (!this.node_) { |
| 270 this.finish_(); |
| 271 return; |
| 272 } |
| 273 |
| 274 var root = AutomationUtil.getTopLevelRoot(this.node_); |
| 275 if (!root) { |
| 276 this.finish_(); |
| 277 return; |
| 278 } |
| 279 |
| 280 this.walker_ = new AutomationTreeWalker( |
| 281 root, |
| 282 constants.Dir.FORWARD, |
| 283 {visit: function(node) { |
| 284 return !AutomationPredicate.shouldIgnoreNode(node); |
| 285 }}); |
| 286 this.nodeCount_ = 0; |
| 287 this.selectNext_ = false; |
| 288 this.findMoreNodes_(); |
| 289 }, |
| 290 |
| 291 /** |
| 292 * Iterate over nodes from the tree walker. If a node matches the |
| 293 * predicate, add an item to the menu. |
| 294 * |
| 295 * If |this.async_| is true, then after MAX_NODES_BEFORE_ASYNC nodes |
| 296 * have been scanned, call setTimeout to defer searching. This frees |
| 297 * up the main event loop to keep the panel menu responsive, otherwise |
| 298 * it basically freezes up until all of the nodes have been found. |
| 299 * @private |
| 300 */ |
| 301 findMoreNodes_: function() { |
| 302 while (this.walker_.next().node) { |
| 303 var node = this.walker_.node; |
| 304 if (node == this.node_) |
| 305 this.selectNext_ = true; |
| 306 if (this.pred_(node)) { |
| 307 var output = new Output(); |
| 308 var range = cursors.Range.fromNode(node); |
| 309 output.withSpeech(range, range, Output.EventType.NAVIGATE); |
| 310 var label = output.toString(); |
| 311 this.addMenuItem(label, '', function() { |
| 312 chrome.extension.getBackgroundPage().ChromeVoxState |
| 313 .instance['navigateToRange'](cursors.Range.fromNode(node)); |
| 314 }); |
| 315 if (this.selectNext_) { |
| 316 this.activateItem(this.items_.length - 1); |
| 317 this.selectNext_ = false; |
| 318 } |
| 319 } |
| 320 |
| 321 if (this.async_) { |
| 322 this.nodeCount_++; |
| 323 if (this.nodeCount_ >= PanelNodeMenu.MAX_NODES_BEFORE_ASYNC) { |
| 324 this.nodeCount_ = 0; |
| 325 window.setTimeout(this.findMoreNodes_.bind(this), 0); |
| 326 return; |
| 327 } |
| 328 } |
| 329 } |
| 330 this.finish_(); |
| 331 }, |
| 332 |
| 333 /** |
| 334 * Called when we've finished searching for nodes. If no matches were |
| 335 * found, adds an item to the menu indicating none were found. |
| 336 * @private |
| 337 */ |
| 338 finish_: function() { |
| 339 if (!this.items_.length) { |
| 340 this.addMenuItem( |
| 341 Msgs.getMsg('panel_menu_item_none'), '', function() {}); |
| 342 this.activateItem(0); |
| 343 } |
| 279 } | 344 } |
| 280 }; | 345 }; |
| OLD | NEW |