Bug 850005 - [AccessFu] Make Utils the single owner of a weak reference to the top-level window. r=davidb

- Some additional cleanup and renaming as well.
This commit is contained in:
Eitan Isaacson 2013-03-15 09:50:55 -04:00
parent 3c1af89ecb
commit 4796b9bf78
4 changed files with 154 additions and 146 deletions

View File

@ -26,11 +26,7 @@ this.AccessFu = {
* mode is started.
*/
attach: function attach(aWindow) {
if (this.chromeWin)
// XXX: only supports attaching to one window now.
throw new Error('Only one window could be attached to AccessFu');
this.chromeWin = aWindow;
Utils.init(aWindow);
this.prefsBranch = Cc['@mozilla.org/preferences-service;1']
.getService(Ci.nsIPrefService).getBranch('accessibility.accessfu.');
@ -77,26 +73,26 @@ this.AccessFu = {
Logger.info('enable');
for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
for each (let mm in Utils.AllMessageManagers)
this._loadFrameScript(mm);
// Add stylesheet
let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css';
this.stylesheet = this.chromeWin.document.createProcessingInstruction(
let stylesheet = Utils.win.document.createProcessingInstruction(
'xml-stylesheet', 'href="' + stylesheetURL + '" type="text/css"');
this.chromeWin.document.insertBefore(this.stylesheet,
this.chromeWin.document.firstChild);
Utils.win.document.insertBefore(stylesheet, Utils.win.document.firstChild);
this.stylesheet = Cu.getWeakReference(stylesheet);
Input.attach(this.chromeWin);
Output.attach(this.chromeWin);
TouchAdapter.attach(this.chromeWin);
Input.start();
Output.start();
TouchAdapter.start();
Services.obs.addObserver(this, 'remote-browser-frame-shown', false);
Services.obs.addObserver(this, 'Accessibility:NextObject', false);
Services.obs.addObserver(this, 'Accessibility:PreviousObject', false);
Services.obs.addObserver(this, 'Accessibility:Focus', false);
this.chromeWin.addEventListener('TabOpen', this);
this.chromeWin.addEventListener('TabSelect', this);
Utils.win.addEventListener('TabOpen', this);
Utils.win.addEventListener('TabSelect', this);
},
/**
@ -110,15 +106,17 @@ this.AccessFu = {
Logger.info('disable');
this.chromeWin.document.removeChild(this.stylesheet);
for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
Utils.win.document.removeChild(this.stylesheet.get());
for each (let mm in Utils.AllMessageManagers)
mm.sendAsyncMessage('AccessFu:Stop');
Input.detach();
TouchAdapter.detach(this.chromeWin);
Input.stop();
Output.stop();
TouchAdapter.stop();
this.chromeWin.removeEventListener('TabOpen', this);
this.chromeWin.removeEventListener('TabSelect', this);
Utils.win.removeEventListener('TabOpen', this);
Utils.win.removeEventListener('TabSelect', this);
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
Services.obs.removeObserver(this, 'Accessibility:NextObject');
@ -194,7 +192,7 @@ this.AccessFu = {
case 'Accessibility:Focus':
this._focused = JSON.parse(aData);
if (this._focused) {
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:VirtualCursor',
{action: 'whereIsIt', move: true});
}
@ -234,11 +232,11 @@ this.AccessFu = {
case 'TabSelect':
{
if (this._focused) {
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
// We delay this for half a second so the awesomebar could close,
// and we could use the current coordinates for the content item.
// XXX TODO figure out how to avoid magic wait here.
this.chromeWin.setTimeout(
Utils.win.setTimeout(
function () {
mm.sendAsyncMessage('AccessFu:VirtualCursor', {action: 'whereIsIt'});
}, 500);
@ -250,7 +248,7 @@ this.AccessFu = {
announce: function announce(aAnnouncement) {
this._output(Presentation.announce(aAnnouncement),
Utils.getCurrentBrowser(this.chromeWin));
Utils.CurrentBrowser);
},
// So we don't enable/disable twice
@ -261,11 +259,22 @@ this.AccessFu = {
};
var Output = {
attach: function attach(aWindow) {
this.chromeWin = aWindow;
start: function start() {
Cu.import('resource://gre/modules/Geometry.jsm');
},
stop: function stop() {
if (this.highlightBox) {
Utils.win.document.documentElement.removeChild(this.highlightBox.get());
delete this.highlightBox;
}
if (this.announceBox) {
Utils.win.document.documentElement.removeChild(this.announceBox.get());
delete this.announceBox;
}
},
Speech: function Speech(aDetails, aBrowser) {
for each (let action in aDetails.actions)
Logger.info('tts.' + action.method, '"' + action.data + '"', JSON.stringify(action.options));
@ -275,66 +284,75 @@ var Output = {
switch (aDetails.method) {
case 'showBounds':
{
let highlightBox = null;
if (!this.highlightBox) {
// Add highlight box
this.highlightBox = this.chromeWin.document.
highlightBox = Utils.win.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
this.chromeWin.document.documentElement.appendChild(this.highlightBox);
this.highlightBox.id = 'virtual-cursor-box';
Utils.win.document.documentElement.appendChild(highlightBox);
highlightBox.id = 'virtual-cursor-box';
// Add highlight inset for inner shadow
let inset = this.chromeWin.document.
let inset = Utils.win.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
inset.id = 'virtual-cursor-inset';
this.highlightBox.appendChild(inset);
highlightBox.appendChild(inset);
this.highlightBox = Cu.getWeakReference(highlightBox);
} else {
highlightBox = this.highlightBox.get();
}
let padding = aDetails.padding;
let r = this._adjustBounds(aDetails.bounds, aBrowser);
// First hide it to avoid flickering when changing the style.
this.highlightBox.style.display = 'none';
this.highlightBox.style.top = (r.top - padding) + 'px';
this.highlightBox.style.left = (r.left - padding) + 'px';
this.highlightBox.style.width = (r.width + padding*2) + 'px';
this.highlightBox.style.height = (r.height + padding*2) + 'px';
this.highlightBox.style.display = 'block';
highlightBox.style.display = 'none';
highlightBox.style.top = (r.top - padding) + 'px';
highlightBox.style.left = (r.left - padding) + 'px';
highlightBox.style.width = (r.width + padding*2) + 'px';
highlightBox.style.height = (r.height + padding*2) + 'px';
highlightBox.style.display = 'block';
break;
}
case 'hideBounds':
{
if (this.highlightBox)
this.highlightBox.style.display = 'none';
let highlightBox = this.highlightBox ? this.highlightBox.get() : null;
if (highlightBox)
highlightBox.get().style.display = 'none';
break;
}
case 'showAnnouncement':
{
if (!this.announceBox) {
this.announceBox = this.chromeWin.document.
let announceBox = this.announceBox ? this.announceBox.get() : null;
if (!announceBox) {
announceBox = Utils.win.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
this.announceBox.id = 'announce-box';
this.chromeWin.document.documentElement.appendChild(this.announceBox);
announceBox.id = 'announce-box';
Utils.win.document.documentElement.appendChild(announceBox);
this.announceBox = Cu.getWeakReference(announceBox);
}
this.announceBox.innerHTML = '<div>' + aDetails.text + '</div>';
this.announceBox.classList.add('showing');
announceBox.innerHTML = '<div>' + aDetails.text + '</div>';
announceBox.classList.add('showing');
if (this._announceHideTimeout)
this.chromeWin.clearTimeout(this._announceHideTimeout);
Utils.win.clearTimeout(this._announceHideTimeout);
if (aDetails.duration > 0)
this._announceHideTimeout = this.chromeWin.setTimeout(
this._announceHideTimeout = Utils.win.setTimeout(
function () {
this.announceBox.classList.remove('showing');
announceBox.classList.remove('showing');
this._announceHideTimeout = 0;
}.bind(this), aDetails.duration);
break;
}
case 'hideAnnouncement':
{
this.announceBox.classList.remove('showing');
let announceBox = this.announceBox ? this.announceBox.get() : null;
if (announceBox)
announceBox.classList.remove('showing');
break;
}
}
@ -353,14 +371,14 @@ var Output = {
},
Haptic: function Haptic(aDetails, aBrowser) {
this.chromeWin.navigator.vibrate(aDetails.pattern);
Utils.win.navigator.vibrate(aDetails.pattern);
},
_adjustBounds: function(aJsonBounds, aBrowser) {
let bounds = new Rect(aJsonBounds.left, aJsonBounds.top,
aJsonBounds.right - aJsonBounds.left,
aJsonBounds.bottom - aJsonBounds.top);
let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 };
let vp = Utils.getViewport(Utils.win) || { zoom: 1.0, offsetY: 0 };
let browserOffset = aBrowser.getBoundingClientRect();
return bounds.translate(browserOffset.left, browserOffset.top).
@ -371,15 +389,14 @@ var Output = {
var Input = {
editState: {},
attach: function attach(aWindow) {
this.chromeWin = aWindow;
this.chromeWin.document.addEventListener('keypress', this, true);
this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
start: function start() {
Utils.win.document.addEventListener('keypress', this, true);
Utils.win.addEventListener('mozAccessFuGesture', this, true);
},
detach: function detach() {
this.chromeWin.document.removeEventListener('keypress', this, true);
this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
stop: function stop() {
Utils.win.document.removeEventListener('keypress', this, true);
Utils.win.removeEventListener('mozAccessFuGesture', this, true);
},
handleEvent: function Input_handleEvent(aEvent) {
@ -430,7 +447,7 @@ var Input = {
this.scroll(1);
break;
case 'explore2':
Utils.getCurrentBrowser(this.chromeWin).contentWindow.scrollBy(
Utils.CurrentBrowser.contentWindow.scrollBy(
-aGesture.deltaX, -aGesture.deltaY);
break;
case 'swiperight3':
@ -525,7 +542,7 @@ var Input = {
},
moveCursor: function moveCursor(aAction, aRule, aInputType, aX, aY) {
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:VirtualCursor',
{action: aAction, rule: aRule,
x: aX, y: aY, origin: 'top',
@ -533,7 +550,7 @@ var Input = {
},
activateCurrent: function activateCurrent() {
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:Activate', {});
},
@ -542,7 +559,7 @@ var Input = {
},
scroll: function scroll(aPage, aHorizontal) {
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:Scroll', {page: aPage, horizontal: aHorizontal, origin: 'top'});
},

View File

@ -27,17 +27,6 @@ Presenter.prototype = {
*/
type: 'Base',
/**
* Attach function for presenter.
* @param {ChromeWindow} aWindow Chrome window the presenter could use.
*/
attach: function attach(aWindow) {},
/**
* Detach function.
*/
detach: function detach() {},
/**
* The virtual cursor's position changed.
* @param {PresenterContext} aContext the context object for the new pivot

View File

@ -42,27 +42,23 @@ this.TouchAdapter = {
// The virtual touch ID generated by an Android hover event.
HOVER_ID: 'hover',
attach: function TouchAdapter_attach(aWindow) {
if (this.chromeWin)
return;
start: function TouchAdapter_start() {
Logger.info('TouchAdapter.start');
Logger.info('TouchAdapter.attach');
this.chromeWin = aWindow;
this._touchPoints = {};
this._dwellTimeout = 0;
this._prevGestures = {};
this._lastExploreTime = 0;
this._dpi = this.chromeWin.QueryInterface(Ci.nsIInterfaceRequestor).
this._dpi = Utils.win.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils).displayDPI;
let target = this.chromeWin;
let target = Utils.win;
if (Utils.MozBuildApp == 'b2g') {
this.glass = this.chromeWin.document.
this.glass = Utils.win.document.
createElementNS('http://www.w3.org/1999/xhtml', 'div');
this.glass.id = 'accessfu-glass';
this.chromeWin.document.documentElement.appendChild(this.glass);
Utils.win.document.documentElement.appendChild(this.glass);
target = this.glass;
}
@ -75,16 +71,13 @@ this.TouchAdapter = {
target.addEventListener('touchstart', this, true, true);
if (Utils.OS != 'Android')
Mouse2Touch.attach(aWindow);
Mouse2Touch.start();
},
detach: function TouchAdapter_detach(aWindow) {
if (!this.chromeWin)
return;
stop: function TouchAdapter_stop() {
Logger.info('TouchAdapter.stop');
Logger.info('TouchAdapter.detach');
let target = this.chromeWin;
let target = Utils.win;
if (Utils.MozBuildApp == 'b2g') {
target = this.glass;
@ -100,14 +93,12 @@ this.TouchAdapter = {
target.removeEventListener('touchstart', this, true, true);
if (Utils.OS != 'Android')
Mouse2Touch.detach(aWindow);
delete this.chromeWin;
Mouse2Touch.stop();
},
handleEvent: function TouchAdapter_handleEvent(aEvent) {
if (this._delayedEvent) {
this.chromeWin.clearTimeout(this._delayedEvent);
Utils.win.clearTimeout(this._delayedEvent);
delete this._delayedEvent;
}
@ -127,7 +118,7 @@ this.TouchAdapter = {
this._touchPoints[identifier] = touchPoint;
this._lastExploreTime = timeStamp + this.SWIPE_MAX_DURATION;
}
this._dwellTimeout = this.chromeWin.setTimeout(
this._dwellTimeout = Utils.win.setTimeout(
(function () {
this.compileAndEmit(timeStamp + this.DWELL_THRESHOLD);
}).bind(this), this.DWELL_THRESHOLD);
@ -234,7 +225,7 @@ this.TouchAdapter = {
this._prevGestures[idhash] = details;
}
this.chromeWin.clearTimeout(this._dwellTimeout);
Utils.win.clearTimeout(this._dwellTimeout);
this.cleanupTouches();
return multiDetails;
@ -260,14 +251,14 @@ this.TouchAdapter = {
}
let emit = function emit() {
let evt = this.chromeWin.document.createEvent('CustomEvent');
let evt = Utils.win.document.createEvent('CustomEvent');
evt.initCustomEvent('mozAccessFuGesture', true, true, aDetails);
this.chromeWin.dispatchEvent(evt);
Utils.win.dispatchEvent(evt);
delete this._delayedEvent;
}.bind(this);
if (emitDelay) {
this._delayedEvent = this.chromeWin.setTimeout(emit, emitDelay);
this._delayedEvent = Utils.win.setTimeout(emit, emitDelay);
} else {
emit();
}
@ -387,17 +378,16 @@ var Mouse2Touch = {
mousemove: 'touchmove'
},
attach: function Mouse2Touch_attach(aWindow) {
this.chromeWin = aWindow;
this.chromeWin.addEventListener('mousedown', this, true, true);
this.chromeWin.addEventListener('mouseup', this, true, true);
this.chromeWin.addEventListener('mousemove', this, true, true);
start: function Mouse2Touch_start() {
Utils.win.addEventListener('mousedown', this, true, true);
Utils.win.addEventListener('mouseup', this, true, true);
Utils.win.addEventListener('mousemove', this, true, true);
},
detach: function Mouse2Touch_detach(aWindow) {
this.chromeWin.removeEventListener('mousedown', this, true, true);
this.chromeWin.removeEventListener('mouseup', this, true, true);
this.chromeWin.removeEventListener('mousemove', this, true, true);
stop: function Mouse2Touch_stop() {
Utils.win.removeEventListener('mousedown', this, true, true);
Utils.win.removeEventListener('mouseup', this, true, true);
Utils.win.removeEventListener('mousemove', this, true, true);
},
handleEvent: function Mouse2Touch_handleEvent(aEvent) {
@ -405,16 +395,16 @@ var Mouse2Touch = {
return;
let name = this._MouseToTouchMap[aEvent.type];
let evt = this.chromeWin.document.createEvent("touchevent");
let points = [this.chromeWin.document.createTouch(
this.chromeWin, aEvent.target, 0,
let evt = Utils.win.document.createEvent("touchevent");
let points = [Utils.win.document.createTouch(
Utils.win, aEvent.target, 0,
aEvent.pageX, aEvent.pageY, aEvent.screenX, aEvent.screenY,
aEvent.clientX, aEvent.clientY, 1, 1, 0, 0)];
// Simulate another touch point at a 5px offset when ctrl is pressed.
if (aEvent.ctrlKey)
points.push(this.chromeWin.document.createTouch(
this.chromeWin, aEvent.target, 1,
points.push(Utils.win.document.createTouch(
Utils.win, aEvent.target, 1,
aEvent.pageX + 5, aEvent.pageY + 5,
aEvent.screenX + 5, aEvent.screenY + 5,
aEvent.clientX + 5, aEvent.clientY + 5,
@ -422,20 +412,20 @@ var Mouse2Touch = {
// Simulate another touch point at a -5px offset when alt is pressed.
if (aEvent.altKey)
points.push(this.chromeWin.document.createTouch(
this.chromeWin, aEvent.target, 2,
points.push(Utils.win.document.createTouch(
Utils.win, aEvent.target, 2,
aEvent.pageX - 5, aEvent.pageY - 5,
aEvent.screenX - 5, aEvent.screenY - 5,
aEvent.clientX - 5, aEvent.clientY - 5,
1, 1, 0, 0));
let touches = this.chromeWin.document.createTouchList(points);
let touches = Utils.win.document.createTouchList(points);
if (name == "touchend") {
let empty = this.chromeWin.document.createTouchList();
evt.initTouchEvent(name, true, true, this.chromeWin, 0,
let empty = Utils.win.document.createTouchList();
evt.initTouchEvent(name, true, true, Utils.win, 0,
false, false, false, false, empty, empty, touches);
} else {
evt.initTouchEvent(name, true, true, this.chromeWin, 0,
evt.initTouchEvent(name, true, true, Utils.win, 0,
false, false, false, false, touches, touches, touches);
}
aEvent.target.dispatchEvent(evt);

View File

@ -20,6 +20,18 @@ this.Utils = {
'{a23983c0-fd0e-11dc-95ff-0800200c9a66}': 'mobile/xul'
},
init: function Utils_init(aWindow) {
if (this._win)
// XXX: only supports attaching to one window now.
throw new Error('Only one top-level window could used with AccessFu');
this._win = Cu.getWeakReference(aWindow);
},
get win() {
return this._win.get();
},
get AccRetrieval() {
if (!this._AccRetrieval) {
this._AccRetrieval = Cc['@mozilla.org/accessibleRetrieval;1'].
@ -69,30 +81,48 @@ this.Utils = {
this._AndroidSdkVersion = value;
},
getBrowserApp: function getBrowserApp(aWindow) {
get BrowserApp() {
switch (this.MozBuildApp) {
case 'mobile/android':
return aWindow.BrowserApp;
return this.win.BrowserApp;
case 'browser':
return aWindow.gBrowser;
return this.win.gBrowser;
case 'b2g':
return aWindow.shell;
return this.win.shell;
default:
return null;
}
},
getCurrentBrowser: function getCurrentBrowser(aWindow) {
get CurrentBrowser() {
if (this.MozBuildApp == 'b2g')
return this.getBrowserApp(aWindow).contentBrowser;
return this.getBrowserApp(aWindow).selectedBrowser;
return this.BrowserApp.contentBrowser;
return this.BrowserApp.selectedBrowser;
},
getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
let browser = this.getCurrentBrowser(aWindow);
get CurrentContentDoc() {
let browser = this.CurrentBrowser;
return browser ? browser.contentDocument : null;
},
get AllMessageManagers() {
let messageManagers = [];
for (let i = 0; i < this.win.messageManager.childCount; i++)
messageManagers.push(this.win.messageManager.getChildAt(i));
let document = this.CurrentContentDoc;
if (document) {
let remoteframes = document.querySelectorAll('iframe[remote=true]');
for (let i = 0; i < remoteframes.length; ++i)
messageManagers.push(this.getMessageManager(remoteframes[i]));
}
return messageManagers;
},
getMessageManager: function getMessageManager(aBrowser) {
try {
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
@ -103,24 +133,6 @@ this.Utils = {
}
},
getAllMessageManagers: function getAllMessageManagers(aWindow) {
let messageManagers = [];
for (let i = 0; i < aWindow.messageManager.childCount; i++)
messageManagers.push(aWindow.messageManager.getChildAt(i));
let document = this.getCurrentContentDoc(aWindow);
if (document) {
let remoteframes = document.querySelectorAll('iframe[remote=true]');
for (let i = 0; i < remoteframes.length; ++i)
messageManagers.push(this.getMessageManager(remoteframes[i]));
}
return messageManagers;
},
getViewport: function getViewport(aWindow) {
switch (this.MozBuildApp) {
case 'mobile/android':