Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1006)

Unified Diff: chrome/browser/resources/chromeos/select_to_speak/background.js

Issue 2509883002: Select-to-speak extension code (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/select_to_speak/manifest.json.jinja2 » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/chromeos/select_to_speak/background.js
diff --git a/chrome/browser/resources/chromeos/select_to_speak/background.js b/chrome/browser/resources/chromeos/select_to_speak/background.js
index 6853db73b68936cd3ee35d8b0ac4811bd738f192..5302281f654bed98d2e45772ed579229462c1e8b 100644
--- a/chrome/browser/resources/chromeos/select_to_speak/background.js
+++ b/chrome/browser/resources/chromeos/select_to_speak/background.js
@@ -1,3 +1,183 @@
// Copyright 2016 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.
+
+var RoleType = chrome.automation.RoleType;
+
+/**
+ * @constructor
David Tseng 2016/11/22 19:01:09 Are you planning on compiling this file (at least
dmazzoni 2016/11/28 18:51:12 Yes. I finished adding that to this change now. To
+ */
+SelectToSpeak = function() {
+ this.node_ = null;
+ this.down_ = false;
+
+ chrome.automation.getDesktop(function(d) {
+ d.addEventListener('mousePressed', this.onMousePressed_.bind(this), true);
David Tseng 2016/11/22 19:01:10 chrome.automation.EventType
dmazzoni 2016/11/28 18:51:12 Done.
+ d.addEventListener('mouseDragged', this.onMouseDragged_.bind(this), true);
+ d.addEventListener('mouseMoved', this.onMouseDragged_.bind(this), true);
David Tseng 2016/11/22 19:01:09 onMouseDraggedOrMoved? (or were these supposed to
dmazzoni 2016/11/28 18:51:13 Actually we don't need to handle mouseMoved. Fixed
+ d.addEventListener('mouseReleased', this.onMouseReleased_.bind(this), true);
+ d.addEventListener('mouseCanceled', this.onMouseCanceled_.bind(this), true);
+ }.bind(this));
+};
+
+SelectToSpeak.prototype = {
+ /**
+ * Called when the mouse is pressed and the user is in a mode where
+ * select-to-speak is capturing mouse events (for example holding down
+ * Search).
+ *
+ * @param {!AutomationEvent} evt
+ */
+ onMousePressed_: function(evt) {
+ this.down_ = true;
+ this.mouseStart_ = {x: evt.mouseX, y: evt.mouseY};
+ this.startNode_ = evt.target;
+ this.speechQueue_ = [];
+ chrome.tts.stop();
+ this.onMouseDragged_(evt);
+ },
+
+ /**
+ * Called when the mouse is moved or dragged and the user is in a
+ * mode where select-to-speak is capturing mouse events (for example
David Tseng 2016/11/22 19:01:10 Select to speak only captures if search is down, r
dmazzoni 2016/11/28 18:51:13 Currently yes but I wanted to write the comment in
+ * holding down Search).
+ *
+ * @param {!AutomationEvent} evt
+ */
+ onMouseDragged_: function(evt) {
+ if (!this.down_)
David Tseng 2016/11/22 19:01:10 Doesn't a mouse drag already mean a mouse down? I
dmazzoni 2016/11/28 18:51:12 This is still needed to handle a race condition wh
+ return;
+
+ var rect = {left: Math.floor(this.mouseStart_.x),
+ top: Math.floor(this.mouseStart_.y),
David Tseng 2016/11/22 19:01:10 How does this work if I drag up or to the left? Do
dmazzoni 2016/11/28 18:51:12 Good point. Fixed.
+ width: Math.floor(evt.mouseX - this.mouseStart_.x),
+ height: Math.floor(evt.mouseY - this.mouseStart_.y)};
+ chrome.accessibilityPrivate.setFocusRing([rect]);
+ },
+
+ /**
+ * Called when the mouse is moved or dragged and the user is in a
+ * mode where select-to-speak is capturing mouse events (for example
David Tseng 2016/11/22 19:01:10 Update the comment
dmazzoni 2016/11/28 18:51:12 Done.
+ * holding down Search).
+ *
+ * @param {!AutomationEvent} evt
+ */
+ onMouseReleased_: function(evt) {
+ this.onMouseDragged_(evt);
+ this.down_ = false;
+
+ chrome.accessibilityPrivate.setFocusRing([]);
David Tseng 2016/11/22 19:01:10 I would have expected this to happen when s2s goes
dmazzoni 2016/11/28 18:51:12 That's handled in onMouseCanceled. For onMouseRel
+
+ // Walk up to the nearest window, web area, or dialog that the
+ // hit node is contained inside. Only speak objects within that
+ // container. In the future we might include other container-like
+ // roles here.
+ var root = this.startNode_;
+ while (root.parent &&
+ root.role != RoleType.window &&
+ root.role != RoleType.rootWebArea &&
+ root.role != RoleType.desktop &&
+ root.role != RoleType.dialog) {
+ root = root.parent;
+ }
+
+ var rect = {left: Math.floor(this.mouseStart_.x),
+ top: Math.floor(this.mouseStart_.y),
+ width: Math.floor(evt.mouseX - this.mouseStart_.x),
+ height: Math.floor(evt.mouseY - this.mouseStart_.y)};
+ var nodes = [];
+ this.findAllMatching_(root, rect, nodes);
+
+ this.speechQueue_ = nodes;
+ this.speakNextInQueue_();
+ },
+
+ /**
+ * Called when the user cancels select-to-speak's capturing of mouse
+ * events (for example by releasing Search while the mouse is still down).
+ *
+ * @param {!AutomationEvent} evt
+ */
+ onMouseCanceled_: function(evt) {
+ this.down_ = false;
+ chrome.accessibilityPrivate.setFocusRing([]);
+ this.speechQueue_ = [];
+ chrome.tts.stop();
+ },
+
+ /**
+ * Returns true if |rect1| and |rect2| overlap. The rects must define
+ * left, top, width, and height.
+ * @param {Object} rect1
+ * @param {Object} rect2
+ * @return {boolean} True if the rects overlap.
+ */
+ overlaps_: function(rect1, rect2) {
David Tseng 2016/11/22 19:01:09 No need to define this function in this class; mak
dmazzoni 2016/11/28 18:51:12 Done.
+ var l1 = rect1.left;
+ var r1 = rect1.left + rect1.width;
+ var t1 = rect1.top;
+ var b1 = rect1.top + rect1.height;
+ var l2 = rect2.left;
+ var r2 = rect2.left + rect2.width;
+ var t2 = rect2.top;
+ var b2 = rect2.top + rect2.height;
+ return (l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2);
David Tseng 2016/11/22 19:01:09 Doesn't look right. What happens if the two rects
dmazzoni 2016/11/28 18:51:13 Both of those cases work as long as the rects have
+ },
+
+ /**
+ * Finds all nodes within the subtree rooted at |node| that overlap
+ * a given rectangle.
+ * @param {AutomationNode} The starting node.
+ * @param {Object} rect The bounding box to search.
+ * @param {Array<AutomationNode>} nodes The matching node array to be
+ * populated.
+ * @return {boolean} True if any matches are found.
+ */
+ findAllMatching_: function(node, rect, nodes) {
+ var found = false;
+ for (var c = node.firstChild; c; c = c.nextSibling) {
+ if (this.findAllMatching_(c, rect, nodes))
+ found = true;
+ }
+
+ if (found)
+ return true;
+
+ if (!node.name)
David Tseng 2016/11/22 19:01:10 This is going to give some strange results I think
dmazzoni 2016/11/28 18:51:13 I think it's actually useful that it reads the alt
+ return false;
+
+ if (this.overlaps_(node.location, rect)) {
+ nodes.push(node);
+ return true;
+ }
+
+ return false;
+ },
+
+ /**
+ * Pop the next matching node from the queue and speak and highlight it.
+ */
+ speakNextInQueue_: function() {
+ if (!this.speechQueue_ || this.speechQueue_.length == 0) {
David Tseng 2016/11/22 19:01:10 Looks like type checking would help here. Set this
dmazzoni 2016/11/28 18:51:12 Done.
+ chrome.accessibilityPrivate.setFocusRing([]);
+ return;
+ }
+
+ var node = this.speechQueue_.shift();
+ chrome.accessibilityPrivate.setFocusRing([node.location]);
+ chrome.tts.speak(node.name, {
+ lang: 'en-US',
David Tseng 2016/11/22 19:01:10 Maybe we should think about starting some code sha
dmazzoni 2016/11/28 18:51:13 Agreed, perhaps some common utility libraries.
+ 'enqueue': false,
David Tseng 2016/11/22 19:01:09 Can we let the native tts queue handle things?
dmazzoni 2016/11/28 18:51:12 Done, it just means slightly trickier logic inside
+ onEvent: (function(event) {
+ if (event.type == 'end') {
+ this.speakNextInQueue_();
+ } else if (event.type == 'interrupted' ||
+ event.type == 'cancelled') {
+ chrome.accessibilityPrivate.setFocusRing([]);
+ }
+ }).bind(this)
+ });
+ }
+};
+
+new SelectToSpeak();
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/select_to_speak/manifest.json.jinja2 » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698