| Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
|
| diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
|
| index f160480783052f7ac7af4e9c20cd3572ef3a8094..1cf338574d7c0449dd4441ae759c658b1e1fc0a8 100644
|
| --- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
|
| +++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/panel_menu.js
|
| @@ -9,6 +9,7 @@
|
| goog.provide('PanelMenu');
|
| goog.provide('PanelNodeMenu');
|
|
|
| +goog.require('AutomationTreeWalker');
|
| goog.require('Output');
|
| goog.require('PanelMenuItem');
|
| goog.require('constants');
|
| @@ -229,45 +230,26 @@ PanelMenu.prototype = {
|
| * @param {string} menuMsg The msg id of the menu.
|
| * @param {chrome.automation.AutomationNode} node ChromeVox's current position.
|
| * @param {AutomationPredicate.Unary} pred Filter to use on the document.
|
| + * @param {boolean} async If true, populates the menu asynchronously by
|
| + * posting a task after searching each chunk of nodes.
|
| * @extends {PanelMenu}
|
| * @constructor
|
| */
|
| -PanelNodeMenu = function(menuMsg, node, pred) {
|
| +PanelNodeMenu = function(menuMsg, node, pred, async) {
|
| PanelMenu.call(this, menuMsg);
|
| - var nodes = [];
|
| - var selectNext = false;
|
| - var activeIndex = -1;
|
| - AutomationUtil.findNodePre(node.root, constants.Dir.FORWARD,
|
| - /** @type {AutomationPredicate.Unary} */(function(n) {
|
| - if (n === node)
|
| - selectNext = true;
|
| -
|
| - if (pred(n)) {
|
| - var output = new Output();
|
| - var range = cursors.Range.fromNode(n);
|
| - output.withSpeech(range, range, Output.EventType.NAVIGATE);
|
| - var label = output.toString();
|
| - this.addMenuItem(label, '', function() {
|
| - chrome.extension.getBackgroundPage().ChromeVoxState
|
| - .instance['navigateToRange'](cursors.Range.fromNode(n));
|
| - });
|
| - if (selectNext) {
|
| - activeIndex = this.items_.length - 1;
|
| - selectNext = false;
|
| - }
|
| - }
|
| - }).bind(this)
|
| - );
|
| -
|
| - if (!this.items_.length) {
|
| - this.addMenuItem(
|
| - Msgs.getMsg('panel_menu_item_none'), '', function() {});
|
| - this.activateItem(0);
|
| - }
|
| - if (activeIndex >= 0)
|
| - this.activateItem(activeIndex);
|
| + this.node_ = node;
|
| + this.pred_ = pred;
|
| + this.async_ = async;
|
| + this.populate_();
|
| };
|
|
|
| +/**
|
| + * The number of nodes to search before posting a task to finish
|
| + * searching.
|
| + * @const {number}
|
| + */
|
| +PanelNodeMenu.MAX_NODES_BEFORE_ASYNC = 100;
|
| +
|
| PanelNodeMenu.prototype = {
|
| __proto__: PanelMenu.prototype,
|
|
|
| @@ -276,5 +258,88 @@ PanelNodeMenu.prototype = {
|
| var activeItem = this.activeIndex_;
|
| PanelMenu.prototype.activate.call(this);
|
| this.activateItem(activeItem);
|
| + },
|
| +
|
| + /**
|
| + * Create the AutomationTreeWalker and kick off the search to find
|
| + * nodes that match the predicate for this menu.
|
| + * @private
|
| + */
|
| + populate_: function() {
|
| + if (!this.node_) {
|
| + this.finish_();
|
| + return;
|
| + }
|
| +
|
| + var root = AutomationUtil.getTopLevelRoot(this.node_);
|
| + if (!root) {
|
| + this.finish_();
|
| + return;
|
| + }
|
| +
|
| + this.walker_ = new AutomationTreeWalker(
|
| + root,
|
| + constants.Dir.FORWARD,
|
| + {visit: function(node) {
|
| + return !AutomationPredicate.shouldIgnoreNode(node);
|
| + }});
|
| + this.nodeCount_ = 0;
|
| + this.selectNext_ = false;
|
| + this.findMoreNodes_();
|
| + },
|
| +
|
| + /**
|
| + * Iterate over nodes from the tree walker. If a node matches the
|
| + * predicate, add an item to the menu.
|
| + *
|
| + * If |this.async_| is true, then after MAX_NODES_BEFORE_ASYNC nodes
|
| + * have been scanned, call setTimeout to defer searching. This frees
|
| + * up the main event loop to keep the panel menu responsive, otherwise
|
| + * it basically freezes up until all of the nodes have been found.
|
| + * @private
|
| + */
|
| + findMoreNodes_: function() {
|
| + while (this.walker_.next().node) {
|
| + var node = this.walker_.node;
|
| + if (node == this.node_)
|
| + this.selectNext_ = true;
|
| + if (this.pred_(node)) {
|
| + var output = new Output();
|
| + var range = cursors.Range.fromNode(node);
|
| + output.withSpeech(range, range, Output.EventType.NAVIGATE);
|
| + var label = output.toString();
|
| + this.addMenuItem(label, '', function() {
|
| + chrome.extension.getBackgroundPage().ChromeVoxState
|
| + .instance['navigateToRange'](cursors.Range.fromNode(node));
|
| + });
|
| + if (this.selectNext_) {
|
| + this.activateItem(this.items_.length - 1);
|
| + this.selectNext_ = false;
|
| + }
|
| + }
|
| +
|
| + if (this.async_) {
|
| + this.nodeCount_++;
|
| + if (this.nodeCount_ >= PanelNodeMenu.MAX_NODES_BEFORE_ASYNC) {
|
| + this.nodeCount_ = 0;
|
| + window.setTimeout(this.findMoreNodes_.bind(this), 0);
|
| + return;
|
| + }
|
| + }
|
| + }
|
| + this.finish_();
|
| + },
|
| +
|
| + /**
|
| + * Called when we've finished searching for nodes. If no matches were
|
| + * found, adds an item to the menu indicating none were found.
|
| + * @private
|
| + */
|
| + finish_: function() {
|
| + if (!this.items_.length) {
|
| + this.addMenuItem(
|
| + Msgs.getMsg('panel_menu_item_none'), '', function() {});
|
| + this.activateItem(0);
|
| + }
|
| }
|
| };
|
|
|