author | Eitan Isaacson <eitan@monotonous.org> |
Mon, 20 Aug 2012 18:29:22 -0400 | |
changeset 102936 | a295ff4319fbafd50072a69fd233b95cdb515d2d |
parent 102935 | 3e0e57eb237aac0a5f9c59927c46d7d439e59e7b |
child 102937 | 0d4cb05fb97fa3a59d24a12728c06fc498ffebad |
push id | 23317 |
push user | ryanvm@gmail.com |
push date | Wed, 22 Aug 2012 02:05:02 +0000 |
treeherder | mozilla-central@abc17059522b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | davidb |
bugs | 773749 |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
accessible/src/jsat/AccessFu.jsm | file | annotate | diff | comparison | revisions | |
accessible/src/jsat/Presenters.jsm | file | annotate | diff | comparison | revisions |
--- a/accessible/src/jsat/AccessFu.jsm +++ b/accessible/src/jsat/AccessFu.jsm @@ -45,16 +45,19 @@ var AccessFu = { this.prefsBranch.addObserver('activate', this, false); this.prefsBranch.addObserver('explorebytouch', this, false); this.touchAdapter = TouchAdapter; switch(Utils.MozBuildApp) { case 'mobile/android': Services.obs.addObserver(this, 'Accessibility:Settings', false); + Services.obs.addObserver(this, 'Accessibility:NextObject', false); + Services.obs.addObserver(this, 'Accessibility:PreviousObject', false); + Services.obs.addObserver(this, 'Accessibility:CurrentObject', false); this.touchAdapter = AndroidTouchAdapter; break; case 'b2g': aWindow.addEventListener( 'ContentStart', (function(event) { let content = aWindow.shell.contentBrowser.contentWindow; content.addEventListener('mozContentEvent', this, false, true); @@ -82,20 +85,22 @@ var AccessFu = { let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css'; this.stylesheet = this.chromeWin.document.createProcessingInstruction( 'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"'); this.chromeWin.document.insertBefore(this.stylesheet, this.chromeWin.document.firstChild); this.addPresenter(new VisualPresenter()); // Implicitly add the Android presenter on Android. - if (Utils.MozBuildApp == 'mobile/android') - this.addPresenter(new AndroidPresenter()); - else if (Utils.MozBuildApp == 'b2g') + if (Utils.MozBuildApp == 'mobile/android') { + this._androidPresenter = new AndroidPresenter(); + this.addPresenter(this._androidPresenter); + } else if (Utils.MozBuildApp == 'b2g') { this.addPresenter(new SpeechPresenter()); + } VirtualCursorController.attach(this.chromeWin); Services.obs.addObserver(this, 'accessible-event', false); this.chromeWin.addEventListener('DOMActivate', this, true); this.chromeWin.addEventListener('resize', this, true); this.chromeWin.addEventListener('scroll', this, true); this.chromeWin.addEventListener('TabOpen', this, true); @@ -232,16 +237,27 @@ var AccessFu = { }, observe: function observe(aSubject, aTopic, aData) { switch (aTopic) { case 'Accessibility:Settings': this._processPreferences(JSON.parse(aData).enabled + 0, JSON.parse(aData).exploreByTouch + 0); break; + case 'Accessibility:NextObject': + VirtualCursorController. + moveForward(Utils.getCurrentContentDoc(this.chromeWin)); + break; + case 'Accessibility:PreviousObject': + VirtualCursorController. + moveBackward(Utils.getCurrentContentDoc(this.chromeWin)); + break; + case 'Accessibility:CurrentObject': + this._androidPresenter.accessibilityFocus(); + break; case 'nsPref:changed': this._processPreferences(this.prefsBranch.getIntPref('activate'), this.prefsBranch.getIntPref('explorebytouch')); break; case 'accessible-event': let event; try { event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
--- a/accessible/src/jsat/Presenters.jsm +++ b/accessible/src/jsat/Presenters.jsm @@ -6,16 +6,17 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; Cu.import('resource://gre/modules/accessibility/Utils.jsm'); Cu.import('resource://gre/modules/accessibility/UtteranceGenerator.jsm'); +Cu.import('resource://gre/modules/Geometry.jsm'); var EXPORTED_SYMBOLS = ['VisualPresenter', 'AndroidPresenter', 'DummyAndroidPresenter', 'SpeechPresenter', 'PresenterContext']; /** @@ -132,32 +133,32 @@ VisualPresenter.prototype = { }, detach: function VisualPresenter_detach() { this.highlightBox.parentNode.removeChild(this.highlightBox); this.highlightBox = this.stylesheet = null; }, viewportChanged: function VisualPresenter_viewportChanged() { - if (this._currentObject) - this._highlight(this._currentObject); + if (this._currentContext) + this._highlight(this._currentContext); }, pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) { - this._currentObject = aContext.accessible; + this._currentContext = aContext; if (!aContext.accessible) { this._hide(); return; } try { aContext.accessible.scrollTo( Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE); - this._highlight(aContext.accessible); + this._highlight(aContext); } catch (e) { Logger.error('Failed to get bounds: ' + e); return; } }, tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) { this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE); @@ -170,52 +171,27 @@ VisualPresenter.prototype = { }, // Internals _hide: function _hide() { this.highlightBox.style.display = 'none'; }, - _highlight: function _highlight(aObject) { + _highlight: function _highlight(aContext) { let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 }; - let bounds = this._getBounds(aObject, vp.zoom); + let r = aContext.bounds.scale(vp.zoom, vp.zoom).expandToIntegers(); // First hide it to avoid flickering when changing the style. this.highlightBox.style.display = 'none'; - this.highlightBox.style.top = bounds.top + 'px'; - this.highlightBox.style.left = bounds.left + 'px'; - this.highlightBox.style.width = bounds.width + 'px'; - this.highlightBox.style.height = bounds.height + 'px'; + this.highlightBox.style.top = (r.top - this.BORDER_PADDING) + 'px'; + this.highlightBox.style.left = (r.left - this.BORDER_PADDING) + 'px'; + this.highlightBox.style.width = (r.width + this.BORDER_PADDING*2) + 'px'; + this.highlightBox.style.height = (r.height + this.BORDER_PADDING*2) + 'px'; this.highlightBox.style.display = 'block'; - }, - - _getBounds: function _getBounds(aObject, aZoom, aStart, aEnd) { - let objX = {}, objY = {}, objW = {}, objH = {}; - - if (aEnd >= 0 && aStart >= 0 && aEnd != aStart) { - // TODO: Get bounds for text ranges. Leaving this blank until we have - // proper text navigation in the virtual cursor. - } - - aObject.getBounds(objX, objY, objW, objH); - - // Can't specify relative coords in nsIAccessible.getBounds, so we do it. - let docX = {}, docY = {}; - let docRoot = aObject.rootDocument.QueryInterface(Ci.nsIAccessible); - docRoot.getBounds(docX, docY, {}, {}); - - let rv = { - left: Math.round((objX.value - docX.value - this.BORDER_PADDING) * aZoom), - top: Math.round((objY.value - docY.value - this.BORDER_PADDING) * aZoom), - width: Math.round((objW.value + (this.BORDER_PADDING * 2)) * aZoom), - height: Math.round((objH.value + (this.BORDER_PADDING * 2)) * aZoom) - }; - - return rv; } }; /** * Android presenter. Fires Android a11y events. */ function AndroidPresenter() {} @@ -228,40 +204,49 @@ AndroidPresenter.prototype = { ANDROID_VIEW_LONG_CLICKED: 0x02, ANDROID_VIEW_SELECTED: 0x04, ANDROID_VIEW_FOCUSED: 0x08, ANDROID_VIEW_TEXT_CHANGED: 0x10, ANDROID_WINDOW_STATE_CHANGED: 0x20, ANDROID_VIEW_HOVER_ENTER: 0x80, ANDROID_VIEW_HOVER_EXIT: 0x100, ANDROID_VIEW_SCROLLED: 0x1000, + ANDROID_ANNOUNCEMENT: 0x4000, + ANDROID_VIEW_ACCESSIBILITY_FOCUSED: 0x8000, attach: function AndroidPresenter_attach(aWindow) { this.chromeWin = aWindow; }, pivotChanged: function AndroidPresenter_pivotChanged(aContext, aReason) { if (!aContext.accessible) return; + this._currentContext = aContext; + let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT && Utils.AndroidSdkVersion >= 14); + let focusEventType = (Utils.AndroidSdkVersion >= 16) ? + this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED : + this.ANDROID_VIEW_FOCUSED; if (isExploreByTouch) { // This isn't really used by TalkBack so this is a half-hearted attempt // for now. this.sendMessageToJava({ gecko: { type: 'Accessibility:Event', eventType: this.ANDROID_VIEW_HOVER_EXIT, text: [] } }); } + let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 }; + let bounds = aContext.bounds.scale(vp.zoom, vp.zoom).expandToIntegers(); let output = []; aContext.newAncestry.forEach( function(acc) { output.push.apply(output, UtteranceGenerator.genForObject(acc)); } ); @@ -272,20 +257,19 @@ AndroidPresenter.prototype = { function(acc) { output.push.apply(output, UtteranceGenerator.genForObject(acc)); } ); this.sendMessageToJava({ gecko: { type: 'Accessibility:Event', - eventType: isExploreByTouch ? - this.ANDROID_VIEW_HOVER_ENTER : - this.ANDROID_VIEW_FOCUSED, - text: output + eventType: (isExploreByTouch) ? this.ANDROID_VIEW_HOVER_ENTER : focusEventType, + text: output, + bounds: bounds } }); }, actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) { this.sendMessageToJava({ gecko: { type: 'Accessibility:Event', @@ -355,25 +339,31 @@ AndroidPresenter.prototype = { _appAnnounce: function _appAnnounce(aUtterance) { if (!aUtterance.length) return; this.sendMessageToJava({ gecko: { type: 'Accessibility:Event', - eventType: this.ANDROID_VIEW_TEXT_CHANGED, + eventType: (Utils.AndroidSdkVersion >= 16) ? + this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED, text: aUtterance, addedCount: aUtterance.join(' ').length, removedCount: 0, fromIndex: 0 } }); }, + accessibilityFocus: function AndroidPresenter_accessibilityFocus() { + if (this._currentContext) + this.pivotChanged(this._currentContext); + }, + sendMessageToJava: function AndroidPresenter_sendMessageTojava(aMessage) { return Cc['@mozilla.org/android/bridge;1']. getService(Ci.nsIAndroidBridge). handleGeckoMessage(JSON.stringify(aMessage)); } }; /** @@ -503,16 +493,35 @@ PresenterContext.prototype = { } if (!this._subtreePreOrder) this._subtreePreOrder = traversePreorder(this._accessible); return this._subtreePreOrder; }, + get bounds() { + if (!this._bounds) { + let objX = {}, objY = {}, objW = {}, objH = {}; + + this._accessible.getBounds(objX, objY, objW, objH); + + // Can't specify relative coords in nsIAccessible.getBounds, so we do it. + let docX = {}, docY = {}; + let docRoot = this._accessible.rootDocument. + QueryInterface(Ci.nsIAccessible); + docRoot.getBounds(docX, docY, {}, {}); + + this._bounds = new Rect(objX.value, objY.value, objW.value, objH.value). + translate(-docX.value, -docY.value); + } + + return this._bounds.clone(); + }, + _isDefunct: function _isDefunct(aAccessible) { try { let extstate = {}; aAccessible.getState({}, extstate); return !!(aAccessible.value & Ci.nsIAccessibleStates.EXT_STATE_DEFUNCT); } catch (x) { return true; }