зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to s-c.
This commit is contained in:
Коммит
cea016db0d
|
@ -12,10 +12,9 @@ const Cr = Components.results;
|
|||
var EXPORTED_SYMBOLS = ['AccessFu'];
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/Geometry.jsm');
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/Presenters.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/VirtualCursorController.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/TouchAdapter.jsm');
|
||||
|
||||
const ACCESSFU_DISABLE = 0;
|
||||
|
@ -24,12 +23,9 @@ const ACCESSFU_AUTO = 2;
|
|||
|
||||
var AccessFu = {
|
||||
/**
|
||||
* Attach chrome-layer accessibility functionality to the given chrome window.
|
||||
* If accessibility is enabled on the platform (currently Android-only), then
|
||||
* a special accessibility mode is started (see startup()).
|
||||
* @param {ChromeWindow} aWindow Chrome window to attach to.
|
||||
* @param {boolean} aForceEnabled Skip platform accessibility check and enable
|
||||
* AccessFu.
|
||||
* Initialize chrome-layer accessibility functionality.
|
||||
* If accessibility is enabled on the platform, then a special accessibility
|
||||
* mode is started.
|
||||
*/
|
||||
attach: function attach(aWindow) {
|
||||
if (this.chromeWin)
|
||||
|
@ -38,21 +34,19 @@ var AccessFu = {
|
|||
|
||||
Logger.info('attach');
|
||||
this.chromeWin = aWindow;
|
||||
this.presenters = [];
|
||||
|
||||
this.prefsBranch = Cc['@mozilla.org/preferences-service;1']
|
||||
.getService(Ci.nsIPrefService).getBranch('accessibility.accessfu.');
|
||||
this.prefsBranch.addObserver('activate', this, false);
|
||||
this.prefsBranch.addObserver('explorebytouch', this, false);
|
||||
|
||||
this.touchAdapter = TouchAdapter;
|
||||
|
||||
switch(Utils.MozBuildApp) {
|
||||
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);
|
||||
Cc['@mozilla.org/android/bridge;1'].
|
||||
getService(Ci.nsIAndroidBridge).handleGeckoMessage(
|
||||
JSON.stringify({ gecko: { type: 'Accessibility:Ready' } }));
|
||||
this.touchAdapter = AndroidTouchAdapter;
|
||||
break;
|
||||
case 'b2g':
|
||||
|
@ -67,7 +61,13 @@ var AccessFu = {
|
|||
break;
|
||||
}
|
||||
|
||||
this._processPreferences();
|
||||
try {
|
||||
this._activatePref = this.prefsBranch.getIntPref('activate');
|
||||
} catch (x) {
|
||||
this._activatePref = ACCESSFU_DISABLE;
|
||||
}
|
||||
|
||||
this._enableOrDisable();
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -81,30 +81,24 @@ var AccessFu = {
|
|||
|
||||
Logger.info('enable');
|
||||
|
||||
for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
|
||||
this._loadFrameScript(mm);
|
||||
|
||||
// Add stylesheet
|
||||
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.chromeWin.document.insertBefore(this.stylesheet,
|
||||
this.chromeWin.document.firstChild);
|
||||
|
||||
this.addPresenter(new VisualPresenter());
|
||||
Input.attach(this.chromeWin);
|
||||
Output.attach(this.chromeWin);
|
||||
this.touchAdapter.attach(this.chromeWin);
|
||||
|
||||
// Implicitly add the Android presenter on Android.
|
||||
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);
|
||||
this.chromeWin.addEventListener('focus', this, true);
|
||||
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:CurrentObject', false);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -113,380 +107,377 @@ var AccessFu = {
|
|||
_disable: function _disable() {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
this._enabled = false;
|
||||
|
||||
Logger.info('disable');
|
||||
|
||||
this.chromeWin.document.removeChild(this.stylesheet);
|
||||
for each (let mm in Utils.getAllMessageManagers(this.chromeWin))
|
||||
mm.sendAsyncMessage('AccessFu:Stop');
|
||||
|
||||
this.presenters.forEach(function(p) { p.detach(); });
|
||||
this.presenters = [];
|
||||
Input.detach();
|
||||
|
||||
VirtualCursorController.detach();
|
||||
|
||||
Services.obs.removeObserver(this, 'accessible-event');
|
||||
this.chromeWin.removeEventListener('DOMActivate', this, true);
|
||||
this.chromeWin.removeEventListener('resize', this, true);
|
||||
this.chromeWin.removeEventListener('scroll', this, true);
|
||||
this.chromeWin.removeEventListener('TabOpen', this, true);
|
||||
this.chromeWin.removeEventListener('focus', this, true);
|
||||
Services.obs.removeObserver(this, 'remote-browser-frame-shown');
|
||||
Services.obs.removeObserver(this, 'Accessibility:NextObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:PreviousObject');
|
||||
Services.obs.removeObserver(this, 'Accessibility:CurrentObject');
|
||||
},
|
||||
|
||||
_processPreferences: function _processPreferences(aEnabled, aTouchEnabled) {
|
||||
let accessPref = ACCESSFU_DISABLE;
|
||||
_enableOrDisable: function _enableOrDisable() {
|
||||
try {
|
||||
accessPref = (aEnabled == undefined) ?
|
||||
this.prefsBranch.getIntPref('activate') : aEnabled;
|
||||
if (this._activatePref == ACCESSFU_ENABLE ||
|
||||
this._systemPref && this._activatePref == ACCESSFU_AUTO)
|
||||
this._enable();
|
||||
else
|
||||
this._disable();
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
|
||||
let ebtPref = ACCESSFU_DISABLE;
|
||||
try {
|
||||
ebtPref = (aTouchEnabled == undefined) ?
|
||||
this.prefsBranch.getIntPref('explorebytouch') : aTouchEnabled;
|
||||
} catch (x) {
|
||||
}
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android') {
|
||||
if (accessPref == ACCESSFU_AUTO) {
|
||||
Cc['@mozilla.org/android/bridge;1'].
|
||||
getService(Ci.nsIAndroidBridge).handleGeckoMessage(
|
||||
JSON.stringify({ gecko: { type: 'Accessibility:Ready' } }));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (accessPref == ACCESSFU_ENABLE)
|
||||
this._enable();
|
||||
else
|
||||
this._disable();
|
||||
|
||||
if (ebtPref == ACCESSFU_ENABLE)
|
||||
this.touchAdapter.attach(this.chromeWin);
|
||||
else
|
||||
this.touchAdapter.detach(this.chromeWin);
|
||||
},
|
||||
|
||||
addPresenter: function addPresenter(presenter) {
|
||||
this.presenters.push(presenter);
|
||||
presenter.attach(this.chromeWin);
|
||||
receiveMessage: function receiveMessage(aMessage) {
|
||||
if (Logger.logLevel >= Logger.DEBUG)
|
||||
Logger.debug('Recieved', aMessage.name, JSON.stringify(aMessage.json));
|
||||
|
||||
switch (aMessage.name) {
|
||||
case 'AccessFu:Ready':
|
||||
let mm = Utils.getMessageManager(aMessage.target);
|
||||
mm.sendAsyncMessage('AccessFu:Start',
|
||||
{method: 'start', buildApp: Utils.MozBuildApp});
|
||||
break;
|
||||
case 'AccessFu:Present':
|
||||
try {
|
||||
for each (let presenter in aMessage.json) {
|
||||
Output[presenter.type](presenter.details, aMessage.target);
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
break;
|
||||
case 'AccessFu:Input':
|
||||
Input.setEditState(aMessage.json);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'focus':
|
||||
{
|
||||
if (aEvent.target instanceof Ci.nsIDOMWindow) {
|
||||
let docAcc = getAccessible(aEvent.target.document);
|
||||
let docContext = new PresenterContext(docAcc, null);
|
||||
let cursorable = docAcc.QueryInterface(Ci.nsIAccessibleCursorable);
|
||||
let vcContext = new PresenterContext(
|
||||
(cursorable) ? cursorable.virtualCursor.position : null, null);
|
||||
this.presenters.forEach(
|
||||
function(p) { p.tabSelected(docContext, vcContext); });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'TabOpen':
|
||||
{
|
||||
let browser = aEvent.target.linkedBrowser || aEvent.target;
|
||||
// Store the new browser node. We will need to check later when a new
|
||||
// content document is attached if it has been attached to this new tab.
|
||||
// If it has, than we will need to send a 'loading' message along with
|
||||
// the usual 'newdoc' to presenters.
|
||||
this._pendingDocuments[browser] = true;
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.tabStateChanged(null, 'newtab');
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'DOMActivate':
|
||||
{
|
||||
let activatedAcc = getAccessible(aEvent.originalTarget);
|
||||
let state = {};
|
||||
activatedAcc.getState(state, {});
|
||||
|
||||
// Checkable objects will have a state changed event that we will use
|
||||
// instead of this hackish DOMActivate. We will also know the true
|
||||
// action that was taken.
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_CHECKABLE)
|
||||
return;
|
||||
|
||||
this.presenters.forEach(function(p) {
|
||||
p.actionInvoked(activatedAcc, 'click');
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'scroll':
|
||||
case 'resize':
|
||||
{
|
||||
this.presenters.forEach(function(p) { p.viewportChanged(); });
|
||||
break;
|
||||
}
|
||||
case 'mozContentEvent':
|
||||
{
|
||||
if (aEvent.detail.type == 'accessibility-screenreader') {
|
||||
let pref = aEvent.detail.enabled + 0;
|
||||
this._processPreferences(pref, pref);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
_loadFrameScript: function _loadFrameScript(aMessageManager) {
|
||||
aMessageManager.addMessageListener('AccessFu:Present', this);
|
||||
aMessageManager.addMessageListener('AccessFu:Input', this);
|
||||
aMessageManager.addMessageListener('AccessFu:Ready', this);
|
||||
aMessageManager.
|
||||
loadFrameScript(
|
||||
'chrome://global/content/accessibility/content-script.js', true);
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
Logger.debug('observe', aTopic);
|
||||
switch (aTopic) {
|
||||
case 'Accessibility:Settings':
|
||||
this._processPreferences(JSON.parse(aData).enabled + 0,
|
||||
JSON.parse(aData).exploreByTouch + 0);
|
||||
this._systemPref = JSON.parse(aData).enabled;
|
||||
this._enableOrDisable();
|
||||
break;
|
||||
case 'Accessibility:NextObject':
|
||||
VirtualCursorController.
|
||||
moveForward(Utils.getCurrentContentDoc(this.chromeWin));
|
||||
Input.moveCursor('moveNext', 'Simple', 'gesture');
|
||||
break;
|
||||
case 'Accessibility:PreviousObject':
|
||||
VirtualCursorController.
|
||||
moveBackward(Utils.getCurrentContentDoc(this.chromeWin));
|
||||
Input.moveCursor('movePrevious', 'Simple', 'gesture');
|
||||
break;
|
||||
case 'Accessibility:CurrentObject':
|
||||
this._androidPresenter.accessibilityFocus();
|
||||
let mm = Utils.getCurrentBrowser(this.chromeWin).
|
||||
frameLoader.messageManager;
|
||||
mm.sendAsyncMessage('AccessFu:VirtualCursor',
|
||||
{action: 'presentLastPivot'});
|
||||
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);
|
||||
this._handleAccEvent(event);
|
||||
} catch (ex) {
|
||||
Logger.error(ex);
|
||||
return;
|
||||
if (aData == 'activate') {
|
||||
this._activatePref = this.prefsBranch.getIntPref('activate');
|
||||
this._enableOrDisable();
|
||||
}
|
||||
break;
|
||||
case 'remote-browser-frame-shown':
|
||||
{
|
||||
this._loadFrameScript(
|
||||
aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleAccEvent: function _handleAccEvent(aEvent) {
|
||||
if (Logger.logLevel <= Logger.DEBUG)
|
||||
Logger.debug(Logger.eventToString(aEvent),
|
||||
Logger.accessibleToString(aEvent.accessible));
|
||||
|
||||
switch (aEvent.eventType) {
|
||||
case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
|
||||
{
|
||||
let pivot = aEvent.accessible.
|
||||
QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
|
||||
let event = aEvent.
|
||||
QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
|
||||
let position = pivot.position;
|
||||
let doc = aEvent.DOMNode;
|
||||
|
||||
let presenterContext =
|
||||
new PresenterContext(position, event.oldAccessible);
|
||||
let reason = event.reason;
|
||||
this.presenters.forEach(
|
||||
function(p) { p.pivotChanged(presenterContext, reason); });
|
||||
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE:
|
||||
{
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
|
||||
if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
|
||||
!(event.isExtraState())) {
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.actionInvoked(aEvent.accessible,
|
||||
event.isEnabled() ? 'check' : 'uncheck');
|
||||
}
|
||||
);
|
||||
}
|
||||
else if (event.state == Ci.nsIAccessibleStates.STATE_BUSY &&
|
||||
!(event.isExtraState()) && event.isEnabled()) {
|
||||
let role = event.accessible.role;
|
||||
if ((role == Ci.nsIAccessibleRole.ROLE_DOCUMENT ||
|
||||
role == Ci.nsIAccessibleRole.ROLE_APPLICATION)) {
|
||||
// An existing document has changed to state "busy", this means
|
||||
// something is loading. Send a 'loading' message to presenters.
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.tabStateChanged(event.accessible, 'loading');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_REORDER:
|
||||
{
|
||||
let acc = aEvent.accessible;
|
||||
if (acc.childCount) {
|
||||
let docAcc = acc.getChildAt(0);
|
||||
if (this._pendingDocuments[aEvent.DOMNode]) {
|
||||
// This is a document in a new tab. Check if it is
|
||||
// in a BUSY state (i.e. loading), and inform presenters.
|
||||
// We need to do this because a state change event will not be
|
||||
// fired when an object is created with the BUSY state.
|
||||
// If this is not a new tab, don't bother because we sent
|
||||
// 'loading' when the previous doc changed its state to BUSY.
|
||||
let state = {};
|
||||
docAcc.getState(state, {});
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_BUSY &&
|
||||
this._isNotChromeDoc(docAcc))
|
||||
this.presenters.forEach(
|
||||
function(p) { p.tabStateChanged(docAcc, 'loading'); }
|
||||
);
|
||||
delete this._pendingDocuments[aEvent.DOMNode];
|
||||
}
|
||||
if (this._isBrowserDoc(docAcc))
|
||||
// A new top-level content document has been attached
|
||||
this.presenters.forEach(
|
||||
function(p) { p.tabStateChanged(docAcc, 'newdoc'); }
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_COMPLETE:
|
||||
{
|
||||
if (this._isNotChromeDoc(aEvent.accessible)) {
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.tabStateChanged(aEvent.accessible, 'loaded');
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_LOAD_STOPPED:
|
||||
{
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.tabStateChanged(aEvent.accessible, 'loadstopped');
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_DOCUMENT_RELOAD:
|
||||
{
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.tabStateChanged(aEvent.accessible, 'reload');
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED:
|
||||
case Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED:
|
||||
{
|
||||
if (aEvent.isFromUserInput) {
|
||||
// XXX support live regions as well.
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
|
||||
let isInserted = event.isInserted();
|
||||
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
text = txtIface.
|
||||
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
|
||||
} catch (x) {
|
||||
// XXX we might have gotten an exception with of a
|
||||
// zero-length text. If we did, ignore it (bug #749810).
|
||||
if (txtIface.characterCount)
|
||||
throw x;
|
||||
}
|
||||
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.textChanged(isInserted, event.start, event.length,
|
||||
text, event.modifiedText);
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_SCROLLING_START:
|
||||
{
|
||||
VirtualCursorController.moveCursorToObject(
|
||||
Utils.getVirtualCursor(aEvent.accessibleDocument), aEvent.accessible);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_FOCUS:
|
||||
{
|
||||
let acc = aEvent.accessible;
|
||||
let doc = aEvent.accessibleDocument;
|
||||
if (acc.role != Ci.nsIAccessibleRole.ROLE_DOCUMENT &&
|
||||
doc.role != Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW)
|
||||
VirtualCursorController.moveCursorToObject(
|
||||
Utils.getVirtualCursor(doc), acc);
|
||||
|
||||
let [,extState] = Utils.getStates(acc);
|
||||
let editableState = extState &
|
||||
(Ci.nsIAccessibleStates.EXT_STATE_EDITABLE |
|
||||
Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE);
|
||||
|
||||
if (editableState != VirtualCursorController.editableState) {
|
||||
if (!VirtualCursorController.editableState)
|
||||
this.presenters.forEach(
|
||||
function(p) {
|
||||
p.editingModeChanged(true);
|
||||
}
|
||||
);
|
||||
}
|
||||
VirtualCursorController.editableState = editableState;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
if (aEvent.type == 'mozContentEvent' &&
|
||||
aEvent.detail.type == 'accessibility-screenreader') {
|
||||
this._systemPref = aEvent.detail.enabled;
|
||||
this._enableOrDisable();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if accessible is a top-level content document (i.e. a child of a XUL
|
||||
* browser node).
|
||||
* @param {nsIAccessible} aDocAcc the accessible to check.
|
||||
* @return {boolean} true if this is a top-level content document.
|
||||
*/
|
||||
_isBrowserDoc: function _isBrowserDoc(aDocAcc) {
|
||||
let parent = aDocAcc.parent;
|
||||
if (!parent)
|
||||
return false;
|
||||
|
||||
let domNode = parent.DOMNode;
|
||||
if (!domNode)
|
||||
return false;
|
||||
|
||||
const ns = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul';
|
||||
return (domNode.localName == 'browser' && domNode.namespaceURI == ns);
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if document is not a local "chrome" document, like about:home.
|
||||
* @param {nsIDOMDocument} aDocument the document to check.
|
||||
* @return {boolean} true if this is not a chrome document.
|
||||
*/
|
||||
_isNotChromeDoc: function _isNotChromeDoc(aDocument) {
|
||||
let location = aDocument.DOMNode.location;
|
||||
if (!location)
|
||||
return false;
|
||||
|
||||
return location.protocol != 'about:';
|
||||
},
|
||||
|
||||
// A hash of documents that don't yet have an accessible tree.
|
||||
_pendingDocuments: {},
|
||||
|
||||
// So we don't enable/disable twice
|
||||
_enabled: false
|
||||
};
|
||||
|
||||
function getAccessible(aNode) {
|
||||
try {
|
||||
return Cc['@mozilla.org/accessibleRetrieval;1'].
|
||||
getService(Ci.nsIAccessibleRetrieval).getAccessibleFor(aNode);
|
||||
} catch (e) {
|
||||
return null;
|
||||
var Output = {
|
||||
attach: function attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
},
|
||||
|
||||
Speech: function Speech(aDetails, aBrowser) {
|
||||
for each (let action in aDetails.actions)
|
||||
Logger.info('tts.' + action.method, '"' + action.data + '"', JSON.stringify(action.options));
|
||||
},
|
||||
|
||||
Visual: function Visual(aDetails, aBrowser) {
|
||||
if (!this.highlightBox) {
|
||||
// Add highlight box
|
||||
this.highlightBox = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
this.chromeWin.document.documentElement.appendChild(this.highlightBox);
|
||||
this.highlightBox.id = 'virtual-cursor-box';
|
||||
|
||||
// Add highlight inset for inner shadow
|
||||
let inset = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
inset.id = 'virtual-cursor-inset';
|
||||
|
||||
this.highlightBox.appendChild(inset);
|
||||
}
|
||||
|
||||
if (aDetails.method == 'show') {
|
||||
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';
|
||||
} else if (aDetails.method == 'hide') {
|
||||
this.highlightBox.style.display = 'none';
|
||||
}
|
||||
},
|
||||
|
||||
Android: function Android(aDetails, aBrowser) {
|
||||
if (!this._bridge)
|
||||
this._bridge = Cc['@mozilla.org/android/bridge;1'].getService(Ci.nsIAndroidBridge);
|
||||
|
||||
for each (let androidEvent in aDetails) {
|
||||
androidEvent.type = 'Accessibility:Event';
|
||||
if (androidEvent.bounds)
|
||||
androidEvent.bounds = this._adjustBounds(androidEvent.bounds, aBrowser);
|
||||
this._bridge.handleGeckoMessage(JSON.stringify({gecko: androidEvent}));
|
||||
}
|
||||
},
|
||||
|
||||
_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 browserOffset = aBrowser.getBoundingClientRect();
|
||||
|
||||
return bounds.translate(browserOffset.left, browserOffset.top).
|
||||
scale(vp.zoom, vp.zoom).expandToIntegers();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var Input = {
|
||||
editState: {},
|
||||
|
||||
attach: function attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
this.chromeWin.document.addEventListener('keypress', this, true);
|
||||
this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
detach: function detach() {
|
||||
this.chromeWin.document.removeEventListener('keypress', this, true);
|
||||
this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
handleEvent: function Input_handleEvent(aEvent) {
|
||||
try {
|
||||
switch (aEvent.type) {
|
||||
case 'keypress':
|
||||
this._handleKeypress(aEvent);
|
||||
break;
|
||||
case 'mozAccessFuGesture':
|
||||
this._handleGesture(aEvent);
|
||||
break;
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
},
|
||||
|
||||
_handleGesture: function _handleGesture(aEvent) {
|
||||
let detail = aEvent.detail;
|
||||
Logger.info('Gesture', detail.type,
|
||||
'(fingers: ' + detail.touches.length + ')');
|
||||
|
||||
if (detail.touches.length == 1) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
this.moveCursor('moveNext', 'Simple', 'gestures');
|
||||
break;
|
||||
case 'swipeleft':
|
||||
this.moveCursor('movePrevious', 'Simple', 'gesture');
|
||||
break;
|
||||
case 'doubletap':
|
||||
this.activateCurrent();
|
||||
break;
|
||||
case 'explore':
|
||||
this.moveCursor('moveToPoint', 'Simple', 'gesture',
|
||||
detail.x, detail.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.touches.length == 3) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
this.scroll(-1, true);
|
||||
break;
|
||||
case 'swipedown':
|
||||
this.scroll(-1);
|
||||
break;
|
||||
case 'swipeleft':
|
||||
this.scroll(1, true);
|
||||
break;
|
||||
case 'swipeup':
|
||||
this.scroll(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeypress: function _handleKeypress(aEvent) {
|
||||
let target = aEvent.target;
|
||||
|
||||
// Ignore keys with modifiers so the content could take advantage of them.
|
||||
if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
|
||||
return;
|
||||
|
||||
switch (aEvent.keyCode) {
|
||||
case 0:
|
||||
// an alphanumeric key was pressed, handle it separately.
|
||||
// If it was pressed with either alt or ctrl, just pass through.
|
||||
// If it was pressed with meta, pass the key on without the meta.
|
||||
if (this.editState.editing)
|
||||
return;
|
||||
|
||||
let key = String.fromCharCode(aEvent.charCode);
|
||||
try {
|
||||
let [methodName, rule] = this.keyMap[key];
|
||||
this.moveCursor(methodName, rule, 'keyboard');
|
||||
} catch (x) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case aEvent.DOM_VK_RIGHT:
|
||||
if (this.editState.editing) {
|
||||
if (!this.editState.atEnd)
|
||||
// Don't move forward if caret is not at end of entry.
|
||||
// XXX: Fix for rtl
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
this.moveCursor(aEvent.shiftKey ? 'moveLast' : 'moveNext', 'Simple', 'keyboard');
|
||||
break;
|
||||
case aEvent.DOM_VK_LEFT:
|
||||
if (this.editState.editing) {
|
||||
if (!this.editState.atStart)
|
||||
// Don't move backward if caret is not at start of entry.
|
||||
// XXX: Fix for rtl
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
this.moveCursor(aEvent.shiftKey ? 'moveFirst' : 'movePrevious', 'Simple', 'keyboard');
|
||||
break;
|
||||
case aEvent.DOM_VK_UP:
|
||||
if (this.editState.multiline) {
|
||||
if (!this.editState.atStart)
|
||||
// Don't blur content if caret is not at start of text area.
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android')
|
||||
// Return focus to native Android browser chrome.
|
||||
Cc['@mozilla.org/android/bridge;1'].
|
||||
getService(Ci.nsIAndroidBridge).handleGeckoMessage(
|
||||
JSON.stringify({ gecko: { type: 'ToggleChrome:Focus' } }));
|
||||
break;
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
if (this.editState.editing)
|
||||
return;
|
||||
this.activateCurrent();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
},
|
||||
|
||||
moveCursor: function moveCursor(aAction, aRule, aInputType, aX, aY) {
|
||||
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
|
||||
mm.sendAsyncMessage('AccessFu:VirtualCursor',
|
||||
{action: aAction, rule: aRule,
|
||||
x: aX, y: aY, origin: 'top',
|
||||
inputType: aInputType});
|
||||
},
|
||||
|
||||
activateCurrent: function activateCurrent() {
|
||||
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
|
||||
mm.sendAsyncMessage('AccessFu:Activate', {});
|
||||
},
|
||||
|
||||
setEditState: function setEditState(aEditState) {
|
||||
this.editState = aEditState;
|
||||
},
|
||||
|
||||
scroll: function scroll(aPage, aHorizontal) {
|
||||
let mm = Utils.getMessageManager(Utils.getCurrentBrowser(this.chromeWin));
|
||||
mm.sendAsyncMessage('AccessFu:Scroll', {page: aPage, horizontal: aHorizontal, origin: 'top'});
|
||||
},
|
||||
|
||||
keyMap: {
|
||||
a: ['moveNext', 'Anchor'],
|
||||
A: ['movePrevious', 'Anchor'],
|
||||
b: ['moveNext', 'Button'],
|
||||
B: ['movePrevious', 'Button'],
|
||||
c: ['moveNext', 'Combobox'],
|
||||
C: ['movePrevious', 'Combobox'],
|
||||
e: ['moveNext', 'Entry'],
|
||||
E: ['movePrevious', 'Entry'],
|
||||
f: ['moveNext', 'FormElement'],
|
||||
F: ['movePrevious', 'FormElement'],
|
||||
g: ['moveNext', 'Graphic'],
|
||||
G: ['movePrevious', 'Graphic'],
|
||||
h: ['moveNext', 'Heading'],
|
||||
H: ['movePrevious', 'Heading'],
|
||||
i: ['moveNext', 'ListItem'],
|
||||
I: ['movePrevious', 'ListItem'],
|
||||
k: ['moveNext', 'Link'],
|
||||
K: ['movePrevious', 'Link'],
|
||||
l: ['moveNext', 'List'],
|
||||
L: ['movePrevious', 'List'],
|
||||
p: ['moveNext', 'PageTab'],
|
||||
P: ['movePrevious', 'PageTab'],
|
||||
r: ['moveNext', 'RadioButton'],
|
||||
R: ['movePrevious', 'RadioButton'],
|
||||
s: ['moveNext', 'Separator'],
|
||||
S: ['movePrevious', 'Separator'],
|
||||
t: ['moveNext', 'Table'],
|
||||
T: ['movePrevious', 'Table'],
|
||||
x: ['moveNext', 'Checkbox'],
|
||||
X: ['movePrevious', 'Checkbox']
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
var Cr = Components.results;
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/Presenters.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
var EXPORTED_SYMBOLS = ['EventManager'];
|
||||
|
||||
var EventManager = {
|
||||
editState: {},
|
||||
|
||||
start: function start(aSendMsgFunc) {
|
||||
try {
|
||||
if (!this._started) {
|
||||
this.sendMsgFunc = aSendMsgFunc || function() {};
|
||||
this.presenters = [new VisualPresenter()];
|
||||
|
||||
if (Utils.MozBuildApp == 'b2g') {
|
||||
this.presenters.push(new SpeechPresenter());
|
||||
} else if (Utils.MozBuildApp == 'mobile/android') {
|
||||
this.presenters.push(new AndroidPresenter());
|
||||
}
|
||||
|
||||
Logger.info('EventManager.start', Utils.MozBuildApp, [p.type for each(p in this.presenters)].join(', '));
|
||||
|
||||
this._started = true;
|
||||
Services.obs.addObserver(this, 'accessible-event', false);
|
||||
}
|
||||
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.tabStateChanged(null, 'newtab');
|
||||
}
|
||||
);
|
||||
} catch (x) {
|
||||
Logger.error('Failed to start EventManager:', x);
|
||||
}
|
||||
},
|
||||
|
||||
stop: function stop() {
|
||||
Services.obs.removeObserver(this, 'accessible-event');
|
||||
this.presenters = [];
|
||||
this._started = false;
|
||||
},
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
try {
|
||||
switch (aEvent.type) {
|
||||
case 'DOMActivate':
|
||||
{
|
||||
let activatedAcc =
|
||||
Utils.AccRetrieval.getAccessibleFor(aEvent.originalTarget);
|
||||
let [state, extState] = Utils.getStates(activatedAcc);
|
||||
|
||||
// Checkable objects will have a state changed event that we will use
|
||||
// instead of this hackish DOMActivate. We will also know the true
|
||||
// action that was taken.
|
||||
if (state & Ci.nsIAccessibleStates.STATE_CHECKABLE)
|
||||
return;
|
||||
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.actionInvoked(activatedAcc, 'click');
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'scroll':
|
||||
case 'resize':
|
||||
{
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.viewportChanged();;
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error('Error handling DOM event:', x);
|
||||
}
|
||||
},
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case 'accessible-event':
|
||||
var event;
|
||||
try {
|
||||
event = aSubject.QueryInterface(Ci.nsIAccessibleEvent);
|
||||
this.handleAccEvent(event);
|
||||
} catch (x) {
|
||||
Logger.error('Error handing accessible event:', x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
presentLastPivot: function presentLastPivot() {
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.presentLastPivot();
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
handleAccEvent: function handleAccEvent(aEvent) {
|
||||
if (Logger.logLevel >= Logger.DEBUG)
|
||||
Logger.debug('A11yEvent', Logger.eventToString(aEvent),
|
||||
Logger.accessibleToString(aEvent.accessible));
|
||||
|
||||
switch (aEvent.eventType) {
|
||||
case Ci.nsIAccessibleEvent.EVENT_VIRTUALCURSOR_CHANGED:
|
||||
{
|
||||
let pivot = aEvent.accessible.
|
||||
QueryInterface(Ci.nsIAccessibleCursorable).virtualCursor;
|
||||
let position = pivot.position;
|
||||
if (position.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME)
|
||||
break;
|
||||
let event = aEvent.
|
||||
QueryInterface(Ci.nsIAccessibleVirtualCursorChangeEvent);
|
||||
let presenterContext =
|
||||
new PresenterContext(position, event.oldAccessible);
|
||||
let reason = event.reason;
|
||||
|
||||
if (this.editState.editing)
|
||||
aEvent.accessibleDocument.takeFocus();
|
||||
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.pivotChanged(presenterContext, reason);
|
||||
}
|
||||
);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_STATE_CHANGE:
|
||||
{
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
|
||||
if (event.state == Ci.nsIAccessibleStates.STATE_CHECKED &&
|
||||
!(event.isExtraState())) {
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.actionInvoked(aEvent.accessible,
|
||||
event.isEnabled() ? 'check' : 'uncheck');
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_SCROLLING_START:
|
||||
{
|
||||
let vc = Utils.getVirtualCursor(aEvent.accessibleDocument);
|
||||
vc.moveNext(TraversalRules.Simple, aEvent.accessible, true);
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED:
|
||||
{
|
||||
let acc = aEvent.accessible;
|
||||
let characterCount = acc.
|
||||
QueryInterface(Ci.nsIAccessibleText).characterCount;
|
||||
let caretOffset = aEvent.
|
||||
QueryInterface(Ci.nsIAccessibleCaretMoveEvent).caretOffset;
|
||||
|
||||
// Update editing state, both for presenter and other things
|
||||
let [,extState] = Utils.getStates(acc);
|
||||
let editState = {
|
||||
editing: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_EDITABLE),
|
||||
multiline: !!(extState & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE),
|
||||
atStart: caretOffset == 0,
|
||||
atEnd: caretOffset == characterCount
|
||||
};
|
||||
|
||||
// Not interesting
|
||||
if (!editState.editing && editState.editing == this.editState.editing)
|
||||
break;
|
||||
|
||||
if (editState.editing != this.editState.editing)
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.editingModeChanged(editState.editing);
|
||||
}
|
||||
);
|
||||
|
||||
if (editState.editing != this.editState.editing ||
|
||||
editState.multiline != this.editState.multiline ||
|
||||
editState.atEnd != this.editState.atEnd ||
|
||||
editState.atStart != this.editState.atStart)
|
||||
this.sendMsgFunc("AccessFu:Input", editState);
|
||||
|
||||
this.editState = editState;
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED:
|
||||
case Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED:
|
||||
{
|
||||
if (aEvent.isFromUserInput) {
|
||||
// XXX support live regions as well.
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
|
||||
let isInserted = event.isInserted();
|
||||
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
text = txtIface.
|
||||
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
|
||||
} catch (x) {
|
||||
// XXX we might have gotten an exception with of a
|
||||
// zero-length text. If we did, ignore it (bug #749810).
|
||||
if (txtIface.characterCount)
|
||||
throw x;
|
||||
}
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.textChanged(isInserted, event.start, event.length,
|
||||
text, event.modifiedText);
|
||||
}
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Ci.nsIAccessibleEvent.EVENT_FOCUS:
|
||||
{
|
||||
// Put vc where the focus is at
|
||||
let acc = aEvent.accessible;
|
||||
let doc = aEvent.accessibleDocument;
|
||||
if (acc.role != Ci.nsIAccessibleRole.ROLE_DOCUMENT &&
|
||||
doc.role != Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW) {
|
||||
let vc = Utils.getVirtualCursor(doc);
|
||||
vc.moveNext(TraversalRules.Simple, acc, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
present: function present(aPresenterFunc) {
|
||||
try {
|
||||
this.sendMsgFunc(
|
||||
"AccessFu:Present",
|
||||
[aPresenterFunc(p) for each (p in this.presenters)].
|
||||
filter(function(d) {return !!d;}));
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
let tabstate = '';
|
||||
|
||||
let loadingState = Ci.nsIWebProgressListener.STATE_TRANSFERRING |
|
||||
Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
|
||||
let loadedState = Ci.nsIWebProgressListener.STATE_STOP |
|
||||
Ci.nsIWebProgressListener.STATE_IS_NETWORK;
|
||||
|
||||
if ((aStateFlags & loadingState) == loadingState) {
|
||||
tabstate = 'loading';
|
||||
} else if ((aStateFlags & loadedState) == loadedState &&
|
||||
!aWebProgress.isLoadingDocument) {
|
||||
tabstate = 'loaded';
|
||||
}
|
||||
|
||||
if (tabstate) {
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(aWebProgress.DOMWindow.document);
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.tabStateChanged(docAcc, tabstate);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
onProgressChange: function onProgressChange() {},
|
||||
|
||||
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(aWebProgress.DOMWindow.document);
|
||||
this.present(
|
||||
function(p) {
|
||||
return p.tabStateChanged(docAcc, 'newdoc');
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
onStatusChange: function onStatusChange() {},
|
||||
|
||||
onSecurityChange: function onSecurityChange() {},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.nsISupports,
|
||||
Ci.nsIObserver])
|
||||
};
|
|
@ -26,6 +26,11 @@ var EXPORTED_SYMBOLS = ['VisualPresenter',
|
|||
function Presenter() {}
|
||||
|
||||
Presenter.prototype = {
|
||||
/**
|
||||
* The type of presenter. Used for matching it with the appropriate output method.
|
||||
*/
|
||||
type: 'Base',
|
||||
|
||||
/**
|
||||
* Attach function for presenter.
|
||||
* @param {ChromeWindow} aWindow Chrome window the presenter could use.
|
||||
|
@ -92,13 +97,19 @@ Presenter.prototype = {
|
|||
/**
|
||||
* The viewport has changed, either a scroll, pan, zoom, or
|
||||
* landscape/portrait toggle.
|
||||
* @param {Window} aWindow window of viewport that changed.
|
||||
*/
|
||||
viewportChanged: function viewportChanged() {},
|
||||
viewportChanged: function viewportChanged(aWindow) {},
|
||||
|
||||
/**
|
||||
* We have entered or left text editing mode.
|
||||
*/
|
||||
editingModeChanged: function editingModeChanged(aIsEditing) {}
|
||||
editingModeChanged: function editingModeChanged(aIsEditing) {},
|
||||
|
||||
/**
|
||||
* Re-present the last pivot change.
|
||||
*/
|
||||
presentLastPivot: function AndroidPresenter_presentLastPivot() {}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -110,83 +121,60 @@ function VisualPresenter() {}
|
|||
VisualPresenter.prototype = {
|
||||
__proto__: Presenter.prototype,
|
||||
|
||||
type: 'Visual',
|
||||
|
||||
/**
|
||||
* The padding in pixels between the object and the highlight border.
|
||||
*/
|
||||
BORDER_PADDING: 2,
|
||||
|
||||
attach: function VisualPresenter_attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
|
||||
// Add highlight box
|
||||
this.highlightBox = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
this.chromeWin.document.documentElement.appendChild(this.highlightBox);
|
||||
this.highlightBox.id = 'virtual-cursor-box';
|
||||
|
||||
// Add highlight inset for inner shadow
|
||||
let inset = this.chromeWin.document.
|
||||
createElementNS('http://www.w3.org/1999/xhtml', 'div');
|
||||
inset.id = 'virtual-cursor-inset';
|
||||
|
||||
this.highlightBox.appendChild(inset);
|
||||
},
|
||||
|
||||
detach: function VisualPresenter_detach() {
|
||||
this.highlightBox.parentNode.removeChild(this.highlightBox);
|
||||
this.highlightBox = this.stylesheet = null;
|
||||
},
|
||||
|
||||
viewportChanged: function VisualPresenter_viewportChanged() {
|
||||
viewportChanged: function VisualPresenter_viewportChanged(aWindow) {
|
||||
if (this._currentContext)
|
||||
this._highlight(this._currentContext);
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
method: 'show',
|
||||
bounds: this._currentContext.bounds,
|
||||
padding: this.BORDER_PADDING
|
||||
}
|
||||
};
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
pivotChanged: function VisualPresenter_pivotChanged(aContext, aReason) {
|
||||
this._currentContext = aContext;
|
||||
|
||||
if (!aContext.accessible) {
|
||||
this._hide();
|
||||
return;
|
||||
}
|
||||
if (!aContext.accessible)
|
||||
return {type: this.type, details: {method: 'hide'}};
|
||||
|
||||
try {
|
||||
aContext.accessible.scrollTo(
|
||||
Ci.nsIAccessibleScrollType.SCROLL_TYPE_ANYWHERE);
|
||||
this._highlight(aContext);
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
method: 'show',
|
||||
bounds: aContext.bounds,
|
||||
padding: this.BORDER_PADDING
|
||||
}
|
||||
};
|
||||
} catch (e) {
|
||||
Logger.error('Failed to get bounds: ' + e);
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
tabSelected: function VisualPresenter_tabSelected(aDocContext, aVCContext) {
|
||||
this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
|
||||
return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
|
||||
},
|
||||
|
||||
tabStateChanged: function VisualPresenter_tabStateChanged(aDocObj,
|
||||
aPageState) {
|
||||
if (aPageState == 'newdoc')
|
||||
this._hide();
|
||||
},
|
||||
return {type: this.type, details: {method: 'hide'}};
|
||||
|
||||
// Internals
|
||||
|
||||
_hide: function _hide() {
|
||||
this.highlightBox.style.display = 'none';
|
||||
},
|
||||
|
||||
_highlight: function _highlight(aContext) {
|
||||
let vp = Utils.getViewport(this.chromeWin) || { zoom: 1.0, offsetY: 0 };
|
||||
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 = (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';
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -199,6 +187,8 @@ function AndroidPresenter() {}
|
|||
AndroidPresenter.prototype = {
|
||||
__proto__: Presenter.prototype,
|
||||
|
||||
type: 'Android',
|
||||
|
||||
// Android AccessibilityEvent type constants.
|
||||
ANDROID_VIEW_CLICKED: 0x01,
|
||||
ANDROID_VIEW_LONG_CLICKED: 0x02,
|
||||
|
@ -212,16 +202,14 @@ AndroidPresenter.prototype = {
|
|||
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;
|
||||
return null;
|
||||
|
||||
this._currentContext = aContext;
|
||||
|
||||
let androidEvents = [];
|
||||
|
||||
let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
|
||||
Utils.AndroidSdkVersion >= 14);
|
||||
let focusEventType = (Utils.AndroidSdkVersion >= 16) ?
|
||||
|
@ -231,17 +219,9 @@ AndroidPresenter.prototype = {
|
|||
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: []
|
||||
}
|
||||
});
|
||||
androidEvents.push({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(
|
||||
|
@ -259,34 +239,34 @@ AndroidPresenter.prototype = {
|
|||
}
|
||||
);
|
||||
|
||||
this.sendMessageToJava({
|
||||
gecko: {
|
||||
type: 'Accessibility:Event',
|
||||
eventType: (isExploreByTouch) ? this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
|
||||
text: output,
|
||||
bounds: bounds
|
||||
}
|
||||
});
|
||||
androidEvents.push({eventType: (isExploreByTouch) ?
|
||||
this.ANDROID_VIEW_HOVER_ENTER : focusEventType,
|
||||
text: output,
|
||||
bounds: aContext.bounds});
|
||||
return {
|
||||
type: this.type,
|
||||
details: androidEvents
|
||||
};
|
||||
},
|
||||
|
||||
actionInvoked: function AndroidPresenter_actionInvoked(aObject, aActionName) {
|
||||
this.sendMessageToJava({
|
||||
gecko: {
|
||||
type: 'Accessibility:Event',
|
||||
return {
|
||||
type: this.type,
|
||||
details: [{
|
||||
eventType: this.ANDROID_VIEW_CLICKED,
|
||||
text: UtteranceGenerator.genForAction(aObject, aActionName)
|
||||
}
|
||||
});
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
tabSelected: function AndroidPresenter_tabSelected(aDocContext, aVCContext) {
|
||||
// Send a pivot change message with the full context utterance for this doc.
|
||||
this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
|
||||
return this.pivotChanged(aVCContext, Ci.nsIAccessiblePivot.REASON_NONE);
|
||||
},
|
||||
|
||||
tabStateChanged: function AndroidPresenter_tabStateChanged(aDocObj,
|
||||
aPageState) {
|
||||
this._appAnnounce(
|
||||
return this._appAnnounce(
|
||||
UtteranceGenerator.genForTabStateChange(aDocObj, aPageState));
|
||||
},
|
||||
|
||||
|
@ -294,12 +274,14 @@ AndroidPresenter.prototype = {
|
|||
aLength, aText,
|
||||
aModifiedText) {
|
||||
let androidEvent = {
|
||||
type: 'Accessibility:Event',
|
||||
eventType: this.ANDROID_VIEW_TEXT_CHANGED,
|
||||
text: [aText],
|
||||
fromIndex: aStart,
|
||||
removedCount: 0,
|
||||
addedCount: 0
|
||||
type: this.type,
|
||||
details: [{
|
||||
eventType: this.ANDROID_VIEW_TEXT_CHANGED,
|
||||
text: [aText],
|
||||
fromIndex: aStart,
|
||||
removedCount: 0,
|
||||
addedCount: 0
|
||||
}]
|
||||
};
|
||||
|
||||
if (aIsInserted) {
|
||||
|
@ -312,71 +294,52 @@ AndroidPresenter.prototype = {
|
|||
aText.substring(0, aStart) + aModifiedText + aText.substring(aStart);
|
||||
}
|
||||
|
||||
this.sendMessageToJava({gecko: androidEvent});
|
||||
return androidEvent;
|
||||
},
|
||||
|
||||
viewportChanged: function AndroidPresenter_viewportChanged() {
|
||||
viewportChanged: function AndroidPresenter_viewportChanged(aWindow) {
|
||||
if (Utils.AndroidSdkVersion < 14)
|
||||
return;
|
||||
return null;
|
||||
|
||||
let win = Utils.getBrowserApp(this.chromeWin).selectedBrowser.contentWindow;
|
||||
this.sendMessageToJava({
|
||||
gecko: {
|
||||
type: 'Accessibility:Event',
|
||||
return {
|
||||
type: this.type,
|
||||
details: [{
|
||||
eventType: this.ANDROID_VIEW_SCROLLED,
|
||||
text: [],
|
||||
scrollX: win.scrollX,
|
||||
scrollY: win.scrollY,
|
||||
maxScrollX: win.scrollMaxX,
|
||||
maxScrollY: win.scrollMaxY
|
||||
}
|
||||
});
|
||||
scrollX: aWindow.scrollX,
|
||||
scrollY: aWindow.scrollY,
|
||||
maxScrollX: aWindow.scrollMaxX,
|
||||
maxScrollY: aWindow.scrollMaxY
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
editingModeChanged: function AndroidPresenter_editingModeChanged(aIsEditing) {
|
||||
this._appAnnounce(UtteranceGenerator.genForEditingMode(aIsEditing));
|
||||
return this._appAnnounce(UtteranceGenerator.genForEditingMode(aIsEditing));
|
||||
},
|
||||
|
||||
_appAnnounce: function _appAnnounce(aUtterance) {
|
||||
if (!aUtterance.length)
|
||||
return;
|
||||
return null;
|
||||
|
||||
this.sendMessageToJava({
|
||||
gecko: {
|
||||
type: 'Accessibility:Event',
|
||||
return {
|
||||
type: this.type,
|
||||
details: [{
|
||||
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() {
|
||||
presentLastPivot: function AndroidPresenter_presentLastPivot() {
|
||||
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));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A dummy Android presenter for desktop testing
|
||||
*/
|
||||
|
||||
function DummyAndroidPresenter() {}
|
||||
|
||||
DummyAndroidPresenter.prototype = {
|
||||
__proto__: AndroidPresenter.prototype,
|
||||
|
||||
sendMessageToJava: function DummyAndroidPresenter_sendMessageToJava(aMsg) {
|
||||
Logger.debug('Android event:\n' + JSON.stringify(aMsg, null, 2));
|
||||
return this.pivotChanged(this._currentContext);
|
||||
else
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -389,10 +352,11 @@ function SpeechPresenter() {}
|
|||
SpeechPresenter.prototype = {
|
||||
__proto__: Presenter.prototype,
|
||||
|
||||
type: 'Speech',
|
||||
|
||||
pivotChanged: function SpeechPresenter_pivotChanged(aContext, aReason) {
|
||||
if (!aContext.accessible)
|
||||
return;
|
||||
return null;
|
||||
|
||||
let output = [];
|
||||
|
||||
|
@ -411,9 +375,17 @@ SpeechPresenter.prototype = {
|
|||
}
|
||||
);
|
||||
|
||||
Logger.info('SPEAK', '"' + output.join(' ') + '"');
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
actions: [
|
||||
{method: 'playEarcon', data: 'tick', options: {}},
|
||||
{method: 'speak', data: output.join(' '), options: {enqueue: true}}
|
||||
]
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* PresenterContext: An object that generates and caches context information
|
||||
|
@ -504,7 +476,8 @@ PresenterContext.prototype = {
|
|||
|
||||
this._accessible.getBounds(objX, objY, objW, objH);
|
||||
|
||||
// Can't specify relative coords in nsIAccessible.getBounds, so we do it.
|
||||
// XXX: OOP content provides a screen offset of 0, while in-process provides a real
|
||||
// offset. Removing the offset and using content-relative coords normalizes this.
|
||||
let docX = {}, docY = {};
|
||||
let docRoot = this._accessible.rootDocument.
|
||||
QueryInterface(Ci.nsIAccessible);
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
var EXPORTED_SYMBOLS = ['TraversalRules'];
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
function BaseTraversalRule(aRoles, aMatchFunc) {
|
||||
this._matchRoles = aRoles;
|
||||
this._matchFunc = aMatchFunc;
|
||||
}
|
||||
|
||||
BaseTraversalRule.prototype = {
|
||||
getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
|
||||
aRules.value = this._matchRoles;
|
||||
return aRules.value.length;
|
||||
},
|
||||
|
||||
preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
|
||||
Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
|
||||
|
||||
match: function BaseTraversalRule_match(aAccessible)
|
||||
{
|
||||
if (aAccessible.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
|
||||
return (aAccessible.childCount) ?
|
||||
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE :
|
||||
Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
}
|
||||
|
||||
if (this._matchFunc)
|
||||
return this._matchFunc(aAccessible);
|
||||
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
|
||||
};
|
||||
|
||||
var gSimpleTraversalRoles =
|
||||
[Ci.nsIAccessibleRole.ROLE_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_LINK,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_GRAPHIC,
|
||||
// XXX: Find a better solution for ROLE_STATICTEXT.
|
||||
// It allows to filter list bullets but at the same time it
|
||||
// filters CSS generated content too as an unwanted side effect.
|
||||
// Ci.nsIAccessibleRole.ROLE_STATICTEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_TEXT_LEAF,
|
||||
Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_PROGRESSBAR,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
// Used for traversing in to child OOP frames.
|
||||
Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME];
|
||||
|
||||
var TraversalRules = {
|
||||
Simple: new BaseTraversalRule(
|
||||
gSimpleTraversalRoles,
|
||||
function Simple_match(aAccessible) {
|
||||
switch (aAccessible.role) {
|
||||
case Ci.nsIAccessibleRole.ROLE_COMBOBOX:
|
||||
// We don't want to ignore the subtree because this is often
|
||||
// where the list box hangs out.
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
case Ci.nsIAccessibleRole.ROLE_TEXT_LEAF:
|
||||
{
|
||||
// Nameless text leaves are boring, skip them.
|
||||
let name = aAccessible.name;
|
||||
if (name && name.trim())
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
else
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
}
|
||||
case Ci.nsIAccessibleRole.ROLE_LINK:
|
||||
// If the link has children we should land on them instead.
|
||||
// Image map links don't have children so we need to match those.
|
||||
if (aAccessible.childCount == 0)
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
else
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
default:
|
||||
// Ignore the subtree, if there is one. So that we don't land on
|
||||
// the same content that was already presented by its parent.
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
|
||||
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
SimpleTouch: new BaseTraversalRule(
|
||||
gSimpleTraversalRoles,
|
||||
function Simple_match(aAccessible) {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
|
||||
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||
}
|
||||
),
|
||||
|
||||
Anchor: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LINK],
|
||||
function Anchor_match(aAccessible)
|
||||
{
|
||||
// We want to ignore links, only focus named anchors.
|
||||
let state = {};
|
||||
let extraState = {};
|
||||
aAccessible.getState(state, extraState);
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
} else {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
}
|
||||
}),
|
||||
|
||||
Button: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID]),
|
||||
|
||||
Combobox: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX]),
|
||||
|
||||
Entry: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT]),
|
||||
|
||||
FormElement: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_SLIDER,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM]),
|
||||
|
||||
Graphic: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_GRAPHIC]),
|
||||
|
||||
Heading: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_HEADING]),
|
||||
|
||||
ListItem: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LISTITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_TERM]),
|
||||
|
||||
Link: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LINK],
|
||||
function Link_match(aAccessible)
|
||||
{
|
||||
// We want to ignore anchors, only focus real links.
|
||||
let state = {};
|
||||
let extraState = {};
|
||||
aAccessible.getState(state, extraState);
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
} else {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
}
|
||||
}),
|
||||
|
||||
List: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LIST,
|
||||
Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST]),
|
||||
|
||||
PageTab: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PAGETAB]),
|
||||
|
||||
RadioButton: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM]),
|
||||
|
||||
Separator: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_SEPARATOR]),
|
||||
|
||||
Table: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_TABLE]),
|
||||
|
||||
Checkbox: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM])
|
||||
};
|
|
@ -29,6 +29,10 @@ var Utils = {
|
|||
return this._AccRetrieval;
|
||||
},
|
||||
|
||||
set MozBuildApp(value) {
|
||||
this._buildApp = value;
|
||||
},
|
||||
|
||||
get MozBuildApp() {
|
||||
if (!this._buildApp)
|
||||
this._buildApp = this._buildAppMap[Services.appinfo.ID];
|
||||
|
@ -41,6 +45,13 @@ var Utils = {
|
|||
return this._OS;
|
||||
},
|
||||
|
||||
get ScriptName() {
|
||||
if (!this._ScriptName)
|
||||
this._ScriptName =
|
||||
(Services.appinfo.processType == 2) ? 'AccessFuContent' : 'AccessFu';
|
||||
return this._ScriptName;
|
||||
},
|
||||
|
||||
get AndroidSdkVersion() {
|
||||
if (!this._AndroidSdkVersion) {
|
||||
let shellVersion = Services.sysinfo.get('shellVersion') || '';
|
||||
|
@ -71,24 +82,41 @@ var Utils = {
|
|||
}
|
||||
},
|
||||
|
||||
getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
|
||||
if (this.MozBuildApp == "b2g")
|
||||
return this.getBrowserApp(aWindow).contentBrowser.contentDocument;
|
||||
return this.getBrowserApp(aWindow).selectedBrowser.contentDocument;
|
||||
getCurrentBrowser: function getCurrentBrowser(aWindow) {
|
||||
if (this.MozBuildApp == 'b2g')
|
||||
return this.getBrowserApp(aWindow).contentBrowser;
|
||||
return this.getBrowserApp(aWindow).selectedBrowser;
|
||||
},
|
||||
|
||||
getAllDocuments: function getAllDocuments(aWindow) {
|
||||
let doc = this.AccRetrieval.
|
||||
getAccessibleFor(this.getCurrentContentDoc(aWindow)).
|
||||
QueryInterface(Ci.nsIAccessibleDocument);
|
||||
let docs = [];
|
||||
function getAllDocuments(aDocument) {
|
||||
docs.push(aDocument.DOMDocument);
|
||||
for (let i = 0; i < aDocument.childDocumentCount; i++)
|
||||
getAllDocuments(aDocument.getChildDocumentAt(i));
|
||||
getCurrentContentDoc: function getCurrentContentDoc(aWindow) {
|
||||
return this.getCurrentBrowser(aWindow).contentDocument;
|
||||
},
|
||||
|
||||
getMessageManager: function getMessageManager(aBrowser) {
|
||||
try {
|
||||
return aBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).
|
||||
frameLoader.messageManager;
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
return null;
|
||||
}
|
||||
getAllDocuments(doc);
|
||||
return docs;
|
||||
},
|
||||
|
||||
getAllMessageManagers: function getAllMessageManagers(aWindow) {
|
||||
let messageManagers = [];
|
||||
|
||||
for (let i = 0; i < aWindow.messageManager.childCount; i++)
|
||||
messageManagers.push(aWindow.messageManager.getChildAt(i));
|
||||
|
||||
let remoteframes = this.getCurrentContentDoc(aWindow).
|
||||
querySelectorAll('iframe[remote=true]');
|
||||
|
||||
for (let i = 0; i < remoteframes.length; ++i)
|
||||
messageManagers.push(this.getMessageManager(remoteframes[i]));
|
||||
|
||||
Logger.info(messageManagers.length);
|
||||
|
||||
return messageManagers;
|
||||
},
|
||||
|
||||
getViewport: function getViewport(aWindow) {
|
||||
|
@ -123,83 +151,6 @@ var Utils = {
|
|||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
scroll: function scroll(aWindow, aPage, aHorizontal) {
|
||||
for each (let doc in this.getAllDocuments(aWindow)) {
|
||||
// First see if we could scroll a window.
|
||||
let win = doc.defaultView;
|
||||
if (!aHorizontal && win.scrollMaxY &&
|
||||
((aPage > 0 && win.scrollY < win.scrollMaxY) ||
|
||||
(aPage < 0 && win.scrollY > 0))) {
|
||||
win.scroll(0, win.innerHeight);
|
||||
return true;
|
||||
} else if (aHorizontal && win.scrollMaxX &&
|
||||
((aPage > 0 && win.scrollX < win.scrollMaxX) ||
|
||||
(aPage < 0 && win.scrollX > 0))) {
|
||||
win.scroll(win.innerWidth, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Second, try to scroll main section or current target if there is no
|
||||
// main section.
|
||||
let main = doc.querySelector('[role=main]') ||
|
||||
doc.querySelector(':target');
|
||||
|
||||
if (main) {
|
||||
if ((!aHorizontal && main.clientHeight < main.scrollHeight) ||
|
||||
(aHorizontal && main.clientWidth < main.scrollWidth)) {
|
||||
let s = win.getComputedStyle(main);
|
||||
if (!aHorizontal) {
|
||||
if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
|
||||
main.scrollTop += aPage * main.clientHeight;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
|
||||
main.scrollLeft += aPage * main.clientWidth;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
changePage: function changePage(aWindow, aPage) {
|
||||
for each (let doc in this.getAllDocuments(aWindow)) {
|
||||
// Get current main section or active target.
|
||||
let main = doc.querySelector('[role=main]') ||
|
||||
doc.querySelector(':target');
|
||||
if (!main)
|
||||
continue;
|
||||
|
||||
let mainAcc = this.AccRetrieval.getAccessibleFor(main);
|
||||
if (!mainAcc)
|
||||
continue;
|
||||
|
||||
let controllers = mainAcc.
|
||||
getRelationByType(Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
|
||||
|
||||
for (var i=0; controllers.targetsCount > i; i++) {
|
||||
let controller = controllers.getTarget(i);
|
||||
// If the section has a controlling slider, it should be considered
|
||||
// the page-turner.
|
||||
if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
|
||||
// Sliders are controlled with ctrl+right/left. I just decided :)
|
||||
let evt = doc.createEvent("KeyboardEvent");
|
||||
evt.initKeyEvent('keypress', true, true, null,
|
||||
true, false, false, false,
|
||||
(aPage > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
|
||||
controller.DOMNode.dispatchEvent(evt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -217,7 +168,8 @@ var Logger = {
|
|||
return;
|
||||
|
||||
let message = Array.prototype.slice.call(arguments, 1).join(' ');
|
||||
dump('[AccessFu] ' + this._LEVEL_NAMES[aLogLevel] + ' ' + message + '\n');
|
||||
dump('[' + Utils.ScriptName + '] ' +
|
||||
this._LEVEL_NAMES[aLogLevel] +' ' + message + '\n');
|
||||
},
|
||||
|
||||
info: function info() {
|
||||
|
|
|
@ -1,434 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
var EXPORTED_SYMBOLS = ['VirtualCursorController'];
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
function BaseTraversalRule(aRoles, aMatchFunc) {
|
||||
this._matchRoles = aRoles;
|
||||
this._matchFunc = aMatchFunc;
|
||||
}
|
||||
|
||||
BaseTraversalRule.prototype = {
|
||||
getMatchRoles: function BaseTraversalRule_getmatchRoles(aRules) {
|
||||
aRules.value = this._matchRoles;
|
||||
return aRules.value.length;
|
||||
},
|
||||
|
||||
preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
|
||||
Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
|
||||
|
||||
match: function BaseTraversalRule_match(aAccessible)
|
||||
{
|
||||
if (this._matchFunc)
|
||||
return this._matchFunc(aAccessible);
|
||||
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIAccessibleTraversalRule])
|
||||
};
|
||||
|
||||
var TraversalRules = {
|
||||
Simple: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_MENUITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_LINK,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_GRAPHIC,
|
||||
// XXX: Find a better solution for ROLE_STATICTEXT.
|
||||
// It allows to filter list bullets but at the same time it
|
||||
// filters CSS generated content too as an unwanted side effect.
|
||||
// Ci.nsIAccessibleRole.ROLE_STATICTEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_TEXT_LEAF,
|
||||
Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_PROGRESSBAR,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONMENU,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY],
|
||||
function Simple_match(aAccessible) {
|
||||
switch (aAccessible.role) {
|
||||
case Ci.nsIAccessibleRole.ROLE_COMBOBOX:
|
||||
// We don't want to ignore the subtree because this is often
|
||||
// where the list box hangs out.
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
case Ci.nsIAccessibleRole.ROLE_TEXT_LEAF:
|
||||
{
|
||||
// Nameless text leaves are boring, skip them.
|
||||
let name = aAccessible.name;
|
||||
if (name && name.trim())
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
else
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
}
|
||||
case Ci.nsIAccessibleRole.ROLE_LINK:
|
||||
// If the link has children we should land on them instead.
|
||||
// Image map links don't have children so we need to match those.
|
||||
if (aAccessible.childCount == 0)
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
else
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
default:
|
||||
// Ignore the subtree, if there is one. So that we don't land on
|
||||
// the same content that was already presented by its parent.
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH |
|
||||
Ci.nsIAccessibleTraversalRule.FILTER_IGNORE_SUBTREE;
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
Anchor: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LINK],
|
||||
function Anchor_match(aAccessible)
|
||||
{
|
||||
// We want to ignore links, only focus named anchors.
|
||||
let state = {};
|
||||
let extraState = {};
|
||||
aAccessible.getState(state, extraState);
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
} else {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
}
|
||||
}),
|
||||
|
||||
Button: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID]),
|
||||
|
||||
Combobox: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX]),
|
||||
|
||||
Entry: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT]),
|
||||
|
||||
FormElement: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PUSHBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_SPINBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_TOGGLE_BUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWN,
|
||||
Ci.nsIAccessibleRole.ROLE_BUTTONDROPDOWNGRID,
|
||||
Ci.nsIAccessibleRole.ROLE_COMBOBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_LISTBOX,
|
||||
Ci.nsIAccessibleRole.ROLE_ENTRY,
|
||||
Ci.nsIAccessibleRole.ROLE_PASSWORD_TEXT,
|
||||
Ci.nsIAccessibleRole.ROLE_PAGETAB,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_SLIDER,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM]),
|
||||
|
||||
Graphic: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_GRAPHIC]),
|
||||
|
||||
Heading: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_HEADING]),
|
||||
|
||||
ListItem: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LISTITEM,
|
||||
Ci.nsIAccessibleRole.ROLE_TERM]),
|
||||
|
||||
Link: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LINK],
|
||||
function Link_match(aAccessible)
|
||||
{
|
||||
// We want to ignore anchors, only focus real links.
|
||||
let state = {};
|
||||
let extraState = {};
|
||||
aAccessible.getState(state, extraState);
|
||||
if (state.value & Ci.nsIAccessibleStates.STATE_LINKED) {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_MATCH;
|
||||
} else {
|
||||
return Ci.nsIAccessibleTraversalRule.FILTER_IGNORE;
|
||||
}
|
||||
}),
|
||||
|
||||
List: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_LIST,
|
||||
Ci.nsIAccessibleRole.ROLE_DEFINITION_LIST]),
|
||||
|
||||
PageTab: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_PAGETAB]),
|
||||
|
||||
RadioButton: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_RADIOBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_RADIO_MENU_ITEM]),
|
||||
|
||||
Separator: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_SEPARATOR]),
|
||||
|
||||
Table: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_TABLE]),
|
||||
|
||||
Checkbox: new BaseTraversalRule(
|
||||
[Ci.nsIAccessibleRole.ROLE_CHECKBUTTON,
|
||||
Ci.nsIAccessibleRole.ROLE_CHECK_MENU_ITEM])
|
||||
};
|
||||
|
||||
var VirtualCursorController = {
|
||||
exploreByTouch: false,
|
||||
editableState: 0,
|
||||
|
||||
attach: function attach(aWindow) {
|
||||
this.chromeWin = aWindow;
|
||||
this.chromeWin.document.addEventListener('keypress', this, true);
|
||||
this.chromeWin.addEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
detach: function detach() {
|
||||
this.chromeWin.document.removeEventListener('keypress', this, true);
|
||||
this.chromeWin.removeEventListener('mozAccessFuGesture', this, true);
|
||||
},
|
||||
|
||||
handleEvent: function VirtualCursorController_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'keypress':
|
||||
this._handleKeypress(aEvent);
|
||||
break;
|
||||
case 'mozAccessFuGesture':
|
||||
this._handleGesture(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_handleGesture: function _handleGesture(aEvent) {
|
||||
let document = Utils.getCurrentContentDoc(this.chromeWin);
|
||||
let detail = aEvent.detail;
|
||||
Logger.info('Gesture', detail.type,
|
||||
'(fingers: ' + detail.touches.length + ')');
|
||||
|
||||
if (detail.touches.length == 1) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
this.moveForward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case 'swipeleft':
|
||||
this.moveBackward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case 'doubletap':
|
||||
this.activateCurrent(document);
|
||||
break;
|
||||
case 'explore':
|
||||
this.moveToPoint(document, detail.x, detail.y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.touches.length == 3) {
|
||||
switch (detail.type) {
|
||||
case 'swiperight':
|
||||
if (!Utils.scroll(this.chromeWin, -1, true))
|
||||
Utils.changePage(this.chromeWin, -1);
|
||||
break;
|
||||
case 'swipedown':
|
||||
Utils.scroll(this.chromeWin, -1);
|
||||
break;
|
||||
case 'swipeleft':
|
||||
if (!Utils.scroll(this.chromeWin, 1, true))
|
||||
Utils.changePage(this.chromeWin, 1);
|
||||
case 'swipeup':
|
||||
Utils.scroll(this.chromeWin, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_handleKeypress: function _handleKeypress(aEvent) {
|
||||
let document = Utils.getCurrentContentDoc(this.chromeWin);
|
||||
let target = aEvent.target;
|
||||
|
||||
// Ignore keys with modifiers so the content could take advantage of them.
|
||||
if (aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
|
||||
return;
|
||||
|
||||
switch (aEvent.keyCode) {
|
||||
case 0:
|
||||
// an alphanumeric key was pressed, handle it separately.
|
||||
// If it was pressed with either alt or ctrl, just pass through.
|
||||
// If it was pressed with meta, pass the key on without the meta.
|
||||
if (this.editableState)
|
||||
return;
|
||||
|
||||
let key = String.fromCharCode(aEvent.charCode);
|
||||
let methodName = '', rule = {};
|
||||
try {
|
||||
[methodName, rule] = this.keyMap[key];
|
||||
} catch (x) {
|
||||
return;
|
||||
}
|
||||
this[methodName](document, false, rule);
|
||||
break;
|
||||
case aEvent.DOM_VK_RIGHT:
|
||||
if (this.editableState) {
|
||||
if (target.selectionEnd != target.textLength)
|
||||
// Don't move forward if caret is not at end of entry.
|
||||
// XXX: Fix for rtl
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
this.moveForward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case aEvent.DOM_VK_LEFT:
|
||||
if (this.editableState) {
|
||||
if (target.selectionEnd != 0)
|
||||
// Don't move backward if caret is not at start of entry.
|
||||
// XXX: Fix for rtl
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
this.moveBackward(document, aEvent.shiftKey);
|
||||
break;
|
||||
case aEvent.DOM_VK_UP:
|
||||
if (this.editableState & Ci.nsIAccessibleStates.EXT_STATE_MULTI_LINE) {
|
||||
if (target.selectionEnd != 0)
|
||||
// Don't blur content if caret is not at start of text area.
|
||||
return;
|
||||
else
|
||||
target.blur();
|
||||
}
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android')
|
||||
// Return focus to native Android browser chrome.
|
||||
Cc['@mozilla.org/android/bridge;1'].
|
||||
getService(Ci.nsIAndroidBridge).handleGeckoMessage(
|
||||
JSON.stringify({ gecko: { type: 'ToggleChrome:Focus' } }));
|
||||
break;
|
||||
case aEvent.DOM_VK_RETURN:
|
||||
case aEvent.DOM_VK_ENTER:
|
||||
if (this.editableState)
|
||||
return;
|
||||
this.activateCurrent(document);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
},
|
||||
|
||||
moveToPoint: function moveToPoint(aDocument, aX, aY) {
|
||||
Utils.getVirtualCursor(aDocument).moveToPoint(TraversalRules.Simple,
|
||||
aX, aY, true);
|
||||
},
|
||||
|
||||
moveForward: function moveForward(aDocument, aLast, aRule) {
|
||||
let virtualCursor = Utils.getVirtualCursor(aDocument);
|
||||
if (aLast) {
|
||||
virtualCursor.moveLast(TraversalRules.Simple);
|
||||
} else {
|
||||
try {
|
||||
virtualCursor.moveNext(aRule || TraversalRules.Simple);
|
||||
} catch (x) {
|
||||
this.moveCursorToObject(
|
||||
virtualCursor,
|
||||
Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
moveBackward: function moveBackward(aDocument, aFirst, aRule) {
|
||||
let virtualCursor = Utils.getVirtualCursor(aDocument);
|
||||
if (aFirst) {
|
||||
virtualCursor.moveFirst(TraversalRules.Simple);
|
||||
} else {
|
||||
try {
|
||||
virtualCursor.movePrevious(aRule || TraversalRules.Simple);
|
||||
} catch (x) {
|
||||
this.moveCursorToObject(
|
||||
virtualCursor,
|
||||
Utils.AccRetrieval.getAccessibleFor(aDocument.activeElement), aRule);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
activateCurrent: function activateCurrent(document) {
|
||||
let virtualCursor = Utils.getVirtualCursor(document);
|
||||
let acc = virtualCursor.position;
|
||||
|
||||
if (acc.actionCount > 0) {
|
||||
acc.doAction(0);
|
||||
} else {
|
||||
// XXX Some mobile widget sets do not expose actions properly
|
||||
// (via ARIA roles, etc.), so we need to generate a click.
|
||||
// Could possibly be made simpler in the future. Maybe core
|
||||
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(this.chromeWin.document);
|
||||
let docX = {}, docY = {}, docW = {}, docH = {};
|
||||
docAcc.getBounds(docX, docY, docW, docH);
|
||||
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
acc.getBounds(objX, objY, objW, objH);
|
||||
|
||||
let x = Math.round((objX.value - docX.value) + objW.value / 2);
|
||||
let y = Math.round((objY.value - docY.value) + objH.value / 2);
|
||||
|
||||
let cwu = this.chromeWin.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
cwu.sendMouseEventToWindow('mousedown', x, y, 0, 1, 0, false);
|
||||
cwu.sendMouseEventToWindow('mouseup', x, y, 0, 1, 0, false);
|
||||
}
|
||||
},
|
||||
|
||||
moveCursorToObject: function moveCursorToObject(aVirtualCursor,
|
||||
aAccessible, aRule) {
|
||||
aVirtualCursor.moveNext(aRule || TraversalRules.Simple, aAccessible, true);
|
||||
},
|
||||
|
||||
keyMap: {
|
||||
a: ['moveForward', TraversalRules.Anchor],
|
||||
A: ['moveBackward', TraversalRules.Anchor],
|
||||
b: ['moveForward', TraversalRules.Button],
|
||||
B: ['moveBackward', TraversalRules.Button],
|
||||
c: ['moveForward', TraversalRules.Combobox],
|
||||
C: ['moveBackward', TraversalRules.Combobox],
|
||||
e: ['moveForward', TraversalRules.Entry],
|
||||
E: ['moveBackward', TraversalRules.Entry],
|
||||
f: ['moveForward', TraversalRules.FormElement],
|
||||
F: ['moveBackward', TraversalRules.FormElement],
|
||||
g: ['moveForward', TraversalRules.Graphic],
|
||||
G: ['moveBackward', TraversalRules.Graphic],
|
||||
h: ['moveForward', TraversalRules.Heading],
|
||||
H: ['moveBackward', TraversalRules.Heading],
|
||||
i: ['moveForward', TraversalRules.ListItem],
|
||||
I: ['moveBackward', TraversalRules.ListItem],
|
||||
k: ['moveForward', TraversalRules.Link],
|
||||
K: ['moveBackward', TraversalRules.Link],
|
||||
l: ['moveForward', TraversalRules.List],
|
||||
L: ['moveBackward', TraversalRules.List],
|
||||
p: ['moveForward', TraversalRules.PageTab],
|
||||
P: ['moveBackward', TraversalRules.PageTab],
|
||||
r: ['moveForward', TraversalRules.RadioButton],
|
||||
R: ['moveBackward', TraversalRules.RadioButton],
|
||||
s: ['moveForward', TraversalRules.Separator],
|
||||
S: ['moveBackward', TraversalRules.Separator],
|
||||
t: ['moveForward', TraversalRules.Table],
|
||||
T: ['moveBackward', TraversalRules.Table],
|
||||
x: ['moveForward', TraversalRules.Checkbox],
|
||||
X: ['moveBackward', TraversalRules.Checkbox]
|
||||
}
|
||||
};
|
|
@ -0,0 +1,253 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
var Cr = Components.results;
|
||||
|
||||
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/EventManager.jsm');
|
||||
Cu.import('resource://gre/modules/accessibility/TraversalRules.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
|
||||
Logger.debug('content-script.js');
|
||||
|
||||
function virtualCursorControl(aMessage) {
|
||||
if (Logger.logLevel >= Logger.DEBUG)
|
||||
Logger.debug(aMessage.name, JSON.stringify(aMessage.json));
|
||||
|
||||
try {
|
||||
let vc = Utils.getVirtualCursor(content.document);
|
||||
let origin = aMessage.json.origin;
|
||||
if (origin != 'child') {
|
||||
if (forwardMessage(vc, aMessage))
|
||||
return;
|
||||
}
|
||||
|
||||
let details = aMessage.json;
|
||||
let rule = TraversalRules[details.rule];
|
||||
let moved = 0;
|
||||
switch (details.action) {
|
||||
case 'moveFirst':
|
||||
case 'moveLast':
|
||||
moved = vc[details.action](rule);
|
||||
break;
|
||||
case 'moveNext':
|
||||
case 'movePrevious':
|
||||
try {
|
||||
if (origin == 'parent' && vc.position == null) {
|
||||
if (details.action == 'moveNext')
|
||||
moved = vc.moveFirst(rule);
|
||||
else
|
||||
moved = vc.moveLast(rule);
|
||||
} else {
|
||||
moved = vc[details.action](rule);
|
||||
}
|
||||
} catch (x) {
|
||||
moved = vc.moveNext(rule, content.document.activeElement, true);
|
||||
}
|
||||
break;
|
||||
case 'moveToPoint':
|
||||
moved = vc.moveToPoint(rule, details.x, details.y, true);
|
||||
break;
|
||||
case 'presentLastPivot':
|
||||
EventManager.presentLastPivot();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (moved == true) {
|
||||
forwardMessage(vc, aMessage);
|
||||
} else if (moved == false && details.action != 'moveToPoint') {
|
||||
if (origin == 'parent') {
|
||||
vc.position = null;
|
||||
}
|
||||
aMessage.json.origin = 'child';
|
||||
sendAsyncMessage('AccessFu:VirtualCursor', aMessage.json);
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
}
|
||||
|
||||
function forwardMessage(aVirtualCursor, aMessage) {
|
||||
try {
|
||||
let acc = aVirtualCursor.position;
|
||||
if (acc && acc.role == Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME) {
|
||||
let mm = Utils.getMessageManager(acc.DOMNode);
|
||||
mm.addMessageListener(aMessage.name, virtualCursorControl);
|
||||
aMessage.json.origin = 'parent';
|
||||
// XXX: OOP content's screen offset is 0,
|
||||
// so we remove the real screen offset here.
|
||||
aMessage.json.x -= content.mozInnerScreenX;
|
||||
aMessage.json.y -= content.mozInnerScreenY;
|
||||
mm.sendAsyncMessage(aMessage.name, aMessage.json);
|
||||
return true;
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.error(x);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function activateCurrent(aMessage) {
|
||||
Logger.debug('activateCurrent');
|
||||
function activateAccessible(aAccessible) {
|
||||
if (aAccessible.actionCount > 0) {
|
||||
aAccessible.doAction(0);
|
||||
} else {
|
||||
// XXX Some mobile widget sets do not expose actions properly
|
||||
// (via ARIA roles, etc.), so we need to generate a click.
|
||||
// Could possibly be made simpler in the future. Maybe core
|
||||
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
|
||||
let docX = {}, docY = {}, docW = {}, docH = {};
|
||||
docAcc.getBounds(docX, docY, docW, docH);
|
||||
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
aAccessible.getBounds(objX, objY, objW, objH);
|
||||
|
||||
let x = Math.round((objX.value - docX.value) + objW.value / 2);
|
||||
let y = Math.round((objY.value - docY.value) + objH.value / 2);
|
||||
|
||||
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
cwu.sendMouseEventToWindow('mousedown', x, y, 0, 1, 0, false);
|
||||
cwu.sendMouseEventToWindow('mouseup', x, y, 0, 1, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
let vc = Utils.getVirtualCursor(content.document);
|
||||
if (!forwardMessage(vc, aMessage))
|
||||
activateAccessible(vc.position);
|
||||
}
|
||||
|
||||
function scroll(aMessage) {
|
||||
let vc = Utils.getVirtualCursor(content.document);
|
||||
|
||||
function tryToScroll() {
|
||||
let horiz = aMessage.json.horizontal;
|
||||
let page = aMessage.json.page;
|
||||
|
||||
// Search up heirarchy for scrollable element.
|
||||
let acc = vc.position;
|
||||
while (acc) {
|
||||
let elem = acc.DOMNode;
|
||||
|
||||
// We will do window scrolling next.
|
||||
if (elem == content.document)
|
||||
break;
|
||||
|
||||
if (!horiz && elem.clientHeight < elem.scrollHeight) {
|
||||
let s = content.getComputedStyle(elem);
|
||||
if (s.overflowY == 'scroll' || s.overflowY == 'auto') {
|
||||
elem.scrollTop += page * elem.clientHeight;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (horiz) {
|
||||
if (elem.clientWidth < elem.scrollWidth) {
|
||||
let s = content.getComputedStyle(elem);
|
||||
if (s.overflowX == 'scroll' || s.overflowX == 'auto') {
|
||||
elem.scrollLeft += page * elem.clientWidth;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
let controllers = acc.
|
||||
getRelationByType(
|
||||
Ci.nsIAccessibleRelation.RELATION_CONTROLLED_BY);
|
||||
for (let i = 0; controllers.targetsCount > i; i++) {
|
||||
let controller = controllers.getTarget(i);
|
||||
// If the section has a controlling slider, it should be considered
|
||||
// the page-turner.
|
||||
if (controller.role == Ci.nsIAccessibleRole.ROLE_SLIDER) {
|
||||
// Sliders are controlled with ctrl+right/left. I just decided :)
|
||||
let evt = content.document.createEvent('KeyboardEvent');
|
||||
evt.initKeyEvent(
|
||||
'keypress', true, true, null,
|
||||
true, false, false, false,
|
||||
(page > 0) ? evt.DOM_VK_RIGHT : evt.DOM_VK_LEFT, 0);
|
||||
controller.DOMNode.dispatchEvent(evt);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
acc = acc.parent;
|
||||
}
|
||||
|
||||
// Scroll window.
|
||||
if (!horiz && content.scrollMaxY &&
|
||||
((page > 0 && content.scrollY < content.scrollMaxY) ||
|
||||
(page < 0 && content.scrollY > 0))) {
|
||||
content.scroll(0, content.innerHeight);
|
||||
return true;
|
||||
} else if (horiz && content.scrollMaxX &&
|
||||
((page > 0 && content.scrollX < content.scrollMaxX) ||
|
||||
(page < 0 && content.scrollX > 0))) {
|
||||
content.scroll(content.innerWidth, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aMessage.json.origin != 'child') {
|
||||
if (forwardMessage(vc, aMessage))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tryToScroll()) {
|
||||
// Failed to scroll anything in this document. Try in parent document.
|
||||
aMessage.json.origin = 'child';
|
||||
sendAsyncMessage('AccessFu:Scroll', aMessage.json);
|
||||
}
|
||||
}
|
||||
|
||||
addMessageListener('AccessFu:VirtualCursor', virtualCursorControl);
|
||||
addMessageListener('AccessFu:Activate', activateCurrent);
|
||||
addMessageListener('AccessFu:Scroll', scroll);
|
||||
|
||||
addMessageListener(
|
||||
'AccessFu:Start',
|
||||
function(m) {
|
||||
if (m.json.buildApp)
|
||||
Utils.MozBuildApp = m.json.buildApp;
|
||||
|
||||
EventManager.start(
|
||||
function sendMessage(aName, aDetails) {
|
||||
sendAsyncMessage(aName, aDetails);
|
||||
});
|
||||
|
||||
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebProgress).
|
||||
addProgressListener(EventManager,
|
||||
(Ci.nsIWebProgress.NOTIFY_STATE_ALL |
|
||||
Ci.nsIWebProgress.NOTIFY_LOCATION));
|
||||
addEventListener('scroll', EventManager, true);
|
||||
addEventListener('resize', EventManager, true);
|
||||
// XXX: Ideally this would be an a11y event. Bug #742280.
|
||||
addEventListener('DOMActivate', EventManager, true);
|
||||
});
|
||||
|
||||
addMessageListener(
|
||||
'AccessFu:Stop',
|
||||
function(m) {
|
||||
Logger.debug('AccessFu:Stop');
|
||||
|
||||
EventManager.stop();
|
||||
|
||||
docShell.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIWebProgress).
|
||||
removeProgressListener(EventManager);
|
||||
removeEventListener('scroll', EventManager, true);
|
||||
removeEventListener('resize', EventManager, true);
|
||||
// XXX: Ideally this would be an a11y event. Bug #742280.
|
||||
removeEventListener('DOMActivate', EventManager, true);
|
||||
});
|
||||
|
||||
sendAsyncMessage('AccessFu:Ready');
|
|
@ -4,3 +4,4 @@
|
|||
|
||||
toolkit.jar:
|
||||
content/global/accessibility/AccessFu.css (AccessFu.css)
|
||||
content/global/accessibility/content-script.js (content-script.js)
|
||||
|
|
|
@ -190,6 +190,9 @@ var shell = {
|
|||
this.isHomeLoaded = false;
|
||||
|
||||
ppmm.addMessageListener("content-handler", this);
|
||||
ppmm.addMessageListener("dial-handler", this);
|
||||
ppmm.addMessageListener("sms-handler", this);
|
||||
ppmm.addMessageListener("mail-handler", this);
|
||||
},
|
||||
|
||||
stop: function shell_stop() {
|
||||
|
@ -394,17 +397,18 @@ var shell = {
|
|||
},
|
||||
|
||||
receiveMessage: function shell_receiveMessage(message) {
|
||||
if (message.name != 'content-handler') {
|
||||
var names = { 'content-handler': 'view',
|
||||
'dial-handler' : 'dial',
|
||||
'mail-handler' : 'new',
|
||||
'sms-handler' : 'new' }
|
||||
|
||||
if (!(message.name in names))
|
||||
return;
|
||||
}
|
||||
let handler = message.json;
|
||||
|
||||
let data = message.data;
|
||||
new MozActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: handler.type,
|
||||
url: handler.url,
|
||||
extras: handler.extras
|
||||
}
|
||||
name: names[message.name],
|
||||
data: data
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -42,6 +42,18 @@ contract @mozilla.org/uriloader/content-handler;1?type=application/pdf {d18d0216
|
|||
component {8b83eabc-7929-47f4-8b48-4dea8d887e4b} PaymentGlue.js
|
||||
contract @mozilla.org/payment/ui-glue;1 {8b83eabc-7929-47f4-8b48-4dea8d887e4b}
|
||||
|
||||
# TelProtocolHandler.js
|
||||
component {782775dd-7351-45ea-aff1-0ffa872cfdd2} TelProtocolHandler.js
|
||||
contract @mozilla.org/network/protocol;1?name=tel {782775dd-7351-45ea-aff1-0ffa872cfdd2}
|
||||
|
||||
# SmsProtocolHandler.js
|
||||
component {81ca20cb-0dad-4e32-8566-979c8998bd73} SmsProtocolHandler.js
|
||||
contract @mozilla.org/network/protocol;1?name=sms {81ca20cb-0dad-4e32-8566-979c8998bd73}
|
||||
|
||||
# MailtoProtocolHandler.js
|
||||
component {50777e53-0331-4366-a191-900999be386c} MailtoProtocolHandler.js
|
||||
contract @mozilla.org/network/protocol;1?name=mailto {50777e53-0331-4366-a191-900999be386c}
|
||||
|
||||
# YoutubeProtocolHandler.js
|
||||
component {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad} YoutubeProtocolHandler.js
|
||||
contract @mozilla.org/network/protocol;1?name=vnd.youtube {c3f1b945-7e71-49c8-95c7-5ae9cc9e2bad}
|
||||
|
|
|
@ -10,8 +10,13 @@ const Cr = Components.results;
|
|||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
|
||||
const LOCAL_DIR = "/data/local";
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Services, "env",
|
||||
"@mozilla.org/process/environment;1",
|
||||
"nsIEnvironment");
|
||||
|
||||
function DirectoryProvider() {
|
||||
}
|
||||
|
||||
|
@ -35,10 +40,40 @@ DirectoryProvider.prototype = {
|
|||
file.initWithPath("/system/b2g");
|
||||
persistent.value = true;
|
||||
return file;
|
||||
} else if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
|
||||
return this.getOSUpdateApplyToDir(persistent);
|
||||
}
|
||||
#endif
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
getOSUpdateApplyToDir: function dp_getOSUpdateApplyToDir(persistent) {
|
||||
// TODO add logic to check available storage space,
|
||||
// and iterate through pref(s) to find alternative dirs if
|
||||
// necessary.
|
||||
|
||||
let path = Services.env.get("EXTERNAL_STORAGE");
|
||||
if (!path) {
|
||||
path = LOCAL_PATH;
|
||||
}
|
||||
|
||||
let dir = Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(Ci.nsILocalFile)
|
||||
dir.initWithPath(path);
|
||||
|
||||
if (!dir.exists() && path != LOCAL_PATH) {
|
||||
// Fallback to LOCAL_PATH if we didn't fallback earlier
|
||||
dir.initWithPath(LOCAL_PATH);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw Cr.NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
dir.appendRelativePath("updates");
|
||||
persistent.value = false;
|
||||
return dir;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
function MailtoProtocolHandler() {
|
||||
}
|
||||
|
||||
MailtoProtocolHandler.prototype = {
|
||||
|
||||
scheme: "mailto",
|
||||
defaultPort: -1,
|
||||
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = aSpec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
newChannel: function Proto_newChannel(aURI) {
|
||||
cpmm.sendAsyncMessage("mail-handler", {
|
||||
URI: aURI.spec,
|
||||
type: "mail" });
|
||||
|
||||
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
|
||||
},
|
||||
|
||||
classID: Components.ID("{50777e53-0331-4366-a191-900999be386c}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
|
||||
};
|
||||
|
||||
let NSGetFactory = XPCOMUtils.generateNSGetFactory([MailtoProtocolHandler]);
|
|
@ -23,13 +23,24 @@ EXTRA_PP_COMPONENTS = \
|
|||
ContentHandler.js \
|
||||
ContentPermissionPrompt.js \
|
||||
DirectoryProvider.js \
|
||||
MailtoProtocolHandler.js \
|
||||
MozKeyboard.js \
|
||||
ProcessGlobal.js \
|
||||
PaymentGlue.js \
|
||||
SmsProtocolHandler.js \
|
||||
TelProtocolHandler.js \
|
||||
YoutubeProtocolHandler.js \
|
||||
RecoveryService.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_JS_MODULES = \
|
||||
TelURIParser.jsm \
|
||||
$(NULL)
|
||||
|
||||
TEST_DIRS = \
|
||||
test \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_UPDATER
|
||||
EXTRA_PP_COMPONENTS += UpdatePrompt.js
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* SmsProtocolHandle.js
|
||||
*
|
||||
* This file implements the URLs for SMS
|
||||
* https://www.rfc-editor.org/rfc/rfc5724.txt
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import("resource:///modules/TelURIParser.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
function SmsProtocolHandler() {
|
||||
}
|
||||
|
||||
SmsProtocolHandler.prototype = {
|
||||
|
||||
scheme: "sms",
|
||||
defaultPort: -1,
|
||||
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = aSpec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
newChannel: function Proto_newChannel(aURI) {
|
||||
let number = TelURIParser.parseURI('sms', aURI.spec);
|
||||
|
||||
if (number) {
|
||||
cpmm.sendAsyncMessage("sms-handler", {
|
||||
number: number,
|
||||
type: "websms/sms" });
|
||||
}
|
||||
|
||||
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
|
||||
},
|
||||
|
||||
classID: Components.ID("{81ca20cb-0dad-4e32-8566-979c8998bd73}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
|
||||
};
|
||||
|
||||
let NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsProtocolHandler]);
|
|
@ -0,0 +1,58 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* TelProtocolHandle.js
|
||||
*
|
||||
* This file implements the URLs for Telephone Calls
|
||||
* https://www.ietf.org/rfc/rfc2806.txt
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import("resource:///modules/TelURIParser.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsIMessageSender");
|
||||
|
||||
function TelProtocolHandler() {
|
||||
}
|
||||
|
||||
TelProtocolHandler.prototype = {
|
||||
|
||||
scheme: "tel",
|
||||
defaultPort: -1,
|
||||
protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_LOADABLE_BY_ANYONE |
|
||||
Ci.nsIProtocolHandler.URI_DOES_NOT_RETURN_DATA,
|
||||
allowPort: function() false,
|
||||
|
||||
newURI: function Proto_newURI(aSpec, aOriginCharset) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = aSpec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
newChannel: function Proto_newChannel(aURI) {
|
||||
let number = TelURIParser.parseURI('tel', aURI.spec);
|
||||
|
||||
if (number) {
|
||||
cpmm.sendAsyncMessage("dial-handler", {
|
||||
number: number,
|
||||
type: "webtelephony/number" });
|
||||
}
|
||||
|
||||
throw Components.results.NS_ERROR_ILLEGAL_VALUE;
|
||||
},
|
||||
|
||||
classID: Components.ID("{782775dd-7351-45ea-aff1-0ffa872cfdd2}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler])
|
||||
};
|
||||
|
||||
let NSGetFactory = XPCOMUtils.generateNSGetFactory([TelProtocolHandler]);
|
|
@ -0,0 +1,120 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let EXPORTED_SYMBOLS = ["TelURIParser"];
|
||||
|
||||
/**
|
||||
* Singleton providing functionality for parsing tel: and sms: URIs
|
||||
*/
|
||||
let TelURIParser = {
|
||||
parseURI: function(scheme, uri) {
|
||||
// Ignore MWI and USSD codes. See 794034.
|
||||
if (uri.indexOf('*') != -1 || uri.indexOf('#') != -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2806.txt
|
||||
let subscriber = uri.slice((scheme + ':').length);
|
||||
|
||||
if (!subscriber.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let number = '';
|
||||
let pos = 0;
|
||||
let len = subscriber.length;
|
||||
|
||||
// visual-separator
|
||||
let visualSeparator = [ '-', '.', '(', ')' ];
|
||||
let digits = [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ];
|
||||
let dtmfDigits = [ '*', '#', 'A', 'B', 'C', 'D' ];
|
||||
let pauseCharacter = [ 'p', 'w' ];
|
||||
|
||||
// global-phone-number
|
||||
if (subscriber[pos] == '+') {
|
||||
number += '+';
|
||||
for (++pos; pos < len; ++pos) {
|
||||
if (visualSeparator.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else if (digits.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// local-phone-number
|
||||
else {
|
||||
for (; pos < len; ++pos) {
|
||||
if (visualSeparator.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else if (digits.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else if (dtmfDigits.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else if (pauseCharacter.indexOf(subscriber[pos]) != -1) {
|
||||
number += subscriber[pos];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// this means error
|
||||
if (!number.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// isdn-subaddress
|
||||
if (subscriber.substring(pos, pos + 6) == ';isub=') {
|
||||
let subaddress = '';
|
||||
|
||||
for (pos += 6; pos < len; ++pos) {
|
||||
if (visualSeparator.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else if (digits.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: ignore subaddress - Bug 795242
|
||||
}
|
||||
|
||||
// post-dial
|
||||
if (subscriber.substring(pos, pos + 7) == ';postd=') {
|
||||
let subaddress = '';
|
||||
|
||||
for (pos += 7; pos < len; ++pos) {
|
||||
if (visualSeparator.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else if (digits.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else if (dtmfDigits.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else if (pauseCharacter.indexOf(subscriber[pos]) != -1) {
|
||||
subaddress += subscriber[pos];
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: ignore subaddress - Bug 795242
|
||||
}
|
||||
|
||||
// area-specific
|
||||
if (subscriber.substring(pos, pos + 15) == ';phone-context=') {
|
||||
pos += 15;
|
||||
|
||||
// global-network-prefix | local-network-prefix | private-prefi
|
||||
number = subscriber.substring(pos, subscriber.length) + number;
|
||||
}
|
||||
}
|
||||
|
||||
return number || null;
|
||||
}
|
||||
};
|
||||
|
|
@ -168,7 +168,7 @@ UpdatePrompt.prototype = {
|
|||
this._applyWaitTimer = this.createTimer(APPLY_WAIT_TIMEOUT);
|
||||
break;
|
||||
case "restart":
|
||||
this.restartProcess();
|
||||
this.finishUpdate();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -178,6 +178,34 @@ UpdatePrompt.prototype = {
|
|||
Services.aus.addDownloadListener(this);
|
||||
},
|
||||
|
||||
finishUpdate: function UP_finishUpdate() {
|
||||
if (!this._update.isOSUpdate) {
|
||||
// Standard gecko+gaia updates will just need to restart the process
|
||||
this.restartProcess();
|
||||
return;
|
||||
}
|
||||
|
||||
let osApplyToDir;
|
||||
try {
|
||||
this._update.QueryInterface(Ci.nsIWritablePropertyBag);
|
||||
osApplyToDir = this._update.getProperty("osApplyToDir");
|
||||
} catch (e) {}
|
||||
|
||||
if (!osApplyToDir) {
|
||||
log("Error: Update has no osApplyToDir");
|
||||
return;
|
||||
}
|
||||
|
||||
let updateFile = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
updateFile.initWithPath(osApplyToDir + "/update.zip");
|
||||
if (!updateFile.exists()) {
|
||||
log("Error: FOTA update not found at " + updateFile.path);
|
||||
return;
|
||||
}
|
||||
|
||||
this.finishOSUpdate(updateFile.path);
|
||||
},
|
||||
|
||||
restartProcess: function UP_restartProcess() {
|
||||
log("Update downloaded, restarting to apply it");
|
||||
|
||||
|
@ -194,11 +222,25 @@ UpdatePrompt.prototype = {
|
|||
);
|
||||
},
|
||||
|
||||
finishOSUpdate: function UP_finishOSUpdate(aOsUpdatePath) {
|
||||
let recoveryService = Cc["@mozilla.org/recovery-service;1"]
|
||||
.getService(Ci.nsIRecoveryService);
|
||||
|
||||
log("Rebooting into recovery to apply FOTA update: " + aOsUpdatePath);
|
||||
|
||||
try {
|
||||
recoveryService.installFotaUpdate(aOsUpdatePath);
|
||||
} catch(e) {
|
||||
log("Error: Couldn't reboot into recovery to apply FOTA update " +
|
||||
aOsUpdatePath);
|
||||
}
|
||||
},
|
||||
|
||||
notify: function UP_notify(aTimer) {
|
||||
if (aTimer == this._applyPromptTimer) {
|
||||
log("Timed out waiting for result, restarting");
|
||||
this._applyPromptTimer = null;
|
||||
this.restartProcess();
|
||||
this.finishUpdate();
|
||||
} else if (aTimer == this._applyWaitTimer) {
|
||||
this._applyWaitTimer = null;
|
||||
this.showUpdatePrompt();
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# vim: noexpandtab ts=8 sw=8
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = @DEPTH@
|
||||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = @relativesrcdir@
|
||||
FAIL_ON_WARNINGS := 1
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MODULE = B2GComponents
|
||||
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function run_test() {
|
||||
Components.utils.import("resource:///modules/TelURIParser.jsm")
|
||||
|
||||
// global-phone-number
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:+1234'), '+1234');
|
||||
|
||||
// global-phone-number => ignored chars
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:+1234_123'), '+1234');
|
||||
|
||||
// global-phone-number => visualSeparator + digits
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:+-.()1234567890'), '+-.()1234567890');
|
||||
|
||||
// local-phone-number
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:1234'), '1234');
|
||||
|
||||
// local-phone-number => visualSeparator + digits + dtmfDigits + pauseCharacter
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:-.()1234567890ABCDpw'), '-.()1234567890ABCDpw');
|
||||
|
||||
// local-phone-number => visualSeparator + digits + dtmfDigits + pauseCharacter + ignored chars
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:-.()1234567890ABCDpw_'), '-.()1234567890ABCDpw');
|
||||
|
||||
// local-phone-number => isdn-subaddress
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:123;isub=123'), '123');
|
||||
|
||||
// local-phone-number => post-dial
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:123;postd=123'), '123');
|
||||
|
||||
// local-phone-number => prefix
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:123;phone-context=+0321'), '+0321123');
|
||||
|
||||
// local-phone-number => isdn-subaddress + post-dial + prefix
|
||||
do_check_eq(TelURIParser.parseURI('tel', 'tel:123;isub=123;postd=123;phone-context=+0321'), '+0321123');
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_bug793310.js]
|
|
@ -52,7 +52,6 @@ function nextTest()
|
|||
{
|
||||
ok(true,"popuphidden " + i)
|
||||
if (i == tests.length) {
|
||||
SimpleTest.finish();
|
||||
return i;
|
||||
}
|
||||
|
||||
|
|
|
@ -61,7 +61,6 @@ function PrivateBrowsingService() {
|
|||
this._obs.addObserver(this, "private-browsing", true);
|
||||
this._obs.addObserver(this, "command-line-startup", true);
|
||||
this._obs.addObserver(this, "sessionstore-browser-state-restored", true);
|
||||
this._obs.addObserver(this, "domwindowopened", true);
|
||||
|
||||
// List of nsIXULWindows we are going to be closing during the transition
|
||||
this._windowsToClose = [];
|
||||
|
@ -497,18 +496,6 @@ PrivateBrowsingService.prototype = {
|
|||
this._notifyIfTransitionComplete();
|
||||
}
|
||||
break;
|
||||
case "domwindowopened":
|
||||
let aWindow = aSubject;
|
||||
let self = this;
|
||||
aWindow.addEventListener("load", function PBS__onWindowLoad(aEvent) {
|
||||
aWindow.removeEventListener("load", arguments.callee);
|
||||
if (aWindow.document
|
||||
.documentElement
|
||||
.getAttribute("windowtype") == "navigator:browser") {
|
||||
self._setPerWindowPBFlag(aWindow, self._inPrivateBrowsing);
|
||||
}
|
||||
}, false);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_webconsole_bug_658368_time_methods.js \
|
||||
browser_webconsole_bug_764572_output_open_url.js \
|
||||
browser_webconsole_bug_622303_persistent_filters.js \
|
||||
browser_webconsole_bug_770099_bad_policyuri.js \
|
||||
browser_webconsole_bug_770099_violation.js \
|
||||
browser_webconsole_window_zombie.js \
|
||||
browser_cached_messages.js \
|
||||
browser_bug664688_sandbox_update_after_navigation.js \
|
||||
|
@ -188,6 +190,10 @@ MOCHITEST_BROWSER_FILES += \
|
|||
test-bug-658368-time-methods.html \
|
||||
test-webconsole-error-observer.html \
|
||||
test-for-of.html \
|
||||
test_bug_770099_violation.html \
|
||||
test_bug_770099_violation.html^headers^ \
|
||||
test_bug_770099_bad_policy_uri.html \
|
||||
test_bug_770099_bad_policy_uri.html^headers^ \
|
||||
test-result-format-as-string.html \
|
||||
test-bug-737873-mixedcontent.html \
|
||||
$(NULL)
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Tests that the Web Console CSP messages are displayed
|
||||
|
||||
const TEST_BAD_POLICY_URI = "https://example.com/browser/browser/devtools/webconsole/test/test_bug_770099_bad_policy_uri.html";
|
||||
|
||||
let hud = undefined;
|
||||
|
||||
function test() {
|
||||
addTab("data:text/html;charset=utf8,Web Console CSP bad policy URI test");
|
||||
browser.addEventListener("load", function _onLoad() {
|
||||
browser.removeEventListener("load", _onLoad, true);
|
||||
openConsole(null, loadDocument);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function loadDocument(theHud) {
|
||||
hud = theHud;
|
||||
hud.jsterm.clearOutput();
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
content.location = TEST_BAD_POLICY_URI;
|
||||
}
|
||||
|
||||
function onLoad(aEvent) {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
testPolicyURIMessage();
|
||||
}
|
||||
|
||||
function testPolicyURIMessage() {
|
||||
let aOutputNode = hud.outputNode;
|
||||
|
||||
waitForSuccess(
|
||||
{
|
||||
name: "CSP policy URI warning displayed successfully",
|
||||
validatorFn: function() {
|
||||
return aOutputNode.querySelector(".webconsole-msg-error");
|
||||
},
|
||||
|
||||
successFn: function() {
|
||||
//tests on the urlnode
|
||||
let node = aOutputNode.querySelector(".webconsole-msg-error");
|
||||
isnot(node.textContent.indexOf("can't fetch policy"), -1,
|
||||
"CSP Policy URI message found");
|
||||
finishTest();
|
||||
},
|
||||
|
||||
failureFn: finishTest,
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// Tests that the Web Console CSP messages are displayed
|
||||
|
||||
const TEST_VIOLATION = "https://example.com/browser/browser/devtools/webconsole/test/test_bug_770099_violation.html";
|
||||
|
||||
let hud = undefined;
|
||||
|
||||
function test() {
|
||||
addTab("data:text/html;charset=utf8,Web Console CSP violation test");
|
||||
browser.addEventListener("load", function _onLoad() {
|
||||
browser.removeEventListener("load", _onLoad, true);
|
||||
openConsole(null, loadDocument);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function loadDocument(theHud){
|
||||
hud = theHud;
|
||||
hud.jsterm.clearOutput()
|
||||
browser.addEventListener("load", onLoad, true);
|
||||
content.location = TEST_VIOLATION;
|
||||
}
|
||||
|
||||
function onLoad(aEvent) {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
testViolationMessage();
|
||||
}
|
||||
|
||||
function testViolationMessage(){
|
||||
let aOutputNode = hud.outputNode;
|
||||
|
||||
waitForSuccess(
|
||||
{
|
||||
name: "CSP policy URI warning displayed successfully",
|
||||
validatorFn: function() {
|
||||
return aOutputNode.querySelector(".webconsole-msg-warn");
|
||||
},
|
||||
|
||||
successFn: function() {
|
||||
//tests on the urlnode
|
||||
let node = aOutputNode.querySelector(".webconsole-msg-warn");
|
||||
isnot(node.textContent.indexOf("violated"), -1,
|
||||
"CSP violation message found");
|
||||
finishTest();
|
||||
},
|
||||
|
||||
failureFn: finishTest,
|
||||
}
|
||||
);
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test for Bug 770099 - bad policy-uri</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
X-Content-Security-Policy: policy-uri http://example.com/some_policy
|
||||
Content-type: text/html; charset=utf-8
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test for Bug 770099 - policy violation</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=770099">Mozilla Bug 770099</a>
|
||||
<img src="http://some.example.com/test.png">
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
X-Content-Security-Policy: default-src 'self'
|
|
@ -107,6 +107,7 @@ def build_one_stage_aux(stage_dir, is_stage_one):
|
|||
inst_dir = stage_dir + "/clang"
|
||||
|
||||
configure_opts = ["--enable-optimized",
|
||||
"--enable-targets=x86,x86_64,arm",
|
||||
"--disable-assertions",
|
||||
"--prefix=%s" % inst_dir,
|
||||
"--with-gcc-toolchain=/tools/gcc-4.5-0moz3"]
|
||||
|
@ -129,6 +130,8 @@ if not os.path.exists(source_dir):
|
|||
os.symlink("../../clang", llvm_source_dir + "/tools/clang")
|
||||
os.symlink("../../compiler-rt", llvm_source_dir + "/projects/compiler-rt")
|
||||
patch("llvm-debug-frame.patch", 1, llvm_source_dir)
|
||||
patch("llvm-deterministic.patch", 1, llvm_source_dir)
|
||||
patch("clang-deterministic.patch", 1, clang_source_dir)
|
||||
if not isDarwin:
|
||||
patch("old-ld-hack.patch", 1, llvm_source_dir)
|
||||
patch("compiler-rt-gnu89-inline.patch", 0, compiler_rt_source_dir)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
diff --git a/include/clang/AST/CXXInheritance.h b/include/clang/AST/CXXInheritance.h
|
||||
index ee6eba7..87bdbe0 100644
|
||||
--- a/include/clang/AST/CXXInheritance.h
|
||||
+++ b/include/clang/AST/CXXInheritance.h
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "clang/AST/TypeOrdering.h"
|
||||
-#include "llvm/ADT/DenseMap.h"
|
||||
+#include "llvm/ADT/MapVector.h"
|
||||
#include "llvm/ADT/SmallSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include <list>
|
||||
@@ -271,15 +271,14 @@ struct UniqueVirtualMethod {
|
||||
/// pair is the virtual method that overrides it (including the
|
||||
/// subobject in which that virtual function occurs).
|
||||
class OverridingMethods {
|
||||
- llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> >
|
||||
- Overrides;
|
||||
+ typedef SmallVector<UniqueVirtualMethod, 4> ValuesT;
|
||||
+ typedef llvm::MapVector<unsigned, ValuesT> MapType;
|
||||
+ MapType Overrides;
|
||||
|
||||
public:
|
||||
// Iterate over the set of subobjects that have overriding methods.
|
||||
- typedef llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> >
|
||||
- ::iterator iterator;
|
||||
- typedef llvm::DenseMap<unsigned, SmallVector<UniqueVirtualMethod, 4> >
|
||||
- ::const_iterator const_iterator;
|
||||
+ typedef MapType::iterator iterator;
|
||||
+ typedef MapType::const_iterator const_iterator;
|
||||
iterator begin() { return Overrides.begin(); }
|
||||
const_iterator begin() const { return Overrides.begin(); }
|
||||
iterator end() { return Overrides.end(); }
|
||||
@@ -357,8 +356,8 @@ public:
|
||||
/// 0 represents the virtua base class subobject of that type, while
|
||||
/// subobject numbers greater than 0 refer to non-virtual base class
|
||||
/// subobjects of that type.
|
||||
-class CXXFinalOverriderMap
|
||||
- : public llvm::DenseMap<const CXXMethodDecl *, OverridingMethods> { };
|
||||
+class CXXFinalOverriderMap
|
||||
+ : public llvm::MapVector<const CXXMethodDecl *, OverridingMethods> { };
|
||||
|
||||
/// \brief A set of all the primary bases for a class.
|
||||
class CXXIndirectPrimaryBaseSet
|
|
@ -0,0 +1,86 @@
|
|||
diff --git a/include/llvm/ADT/MapVector.h b/include/llvm/ADT/MapVector.h
|
||||
new file mode 100644
|
||||
index 0000000..bad207b
|
||||
--- /dev/null
|
||||
+++ b/include/llvm/ADT/MapVector.h
|
||||
@@ -0,0 +1,80 @@
|
||||
+//===- llvm/ADT/MapVector.h - Map with deterministic value order *- C++ -*-===//
|
||||
+//
|
||||
+// The LLVM Compiler Infrastructure
|
||||
+//
|
||||
+// This file is distributed under the University of Illinois Open Source
|
||||
+// License. See LICENSE.TXT for details.
|
||||
+//
|
||||
+//===----------------------------------------------------------------------===//
|
||||
+//
|
||||
+// This file implements a map that provides insertion order iteration. The
|
||||
+// interface is purposefully minimal. The key is assumed to be cheap to copy
|
||||
+// and 2 copies are kept, one for indexing in a DenseMap, one for iteration in
|
||||
+// a std::vector.
|
||||
+//
|
||||
+//===----------------------------------------------------------------------===//
|
||||
+
|
||||
+#ifndef LLVM_ADT_MAPVECTOR_H
|
||||
+#define LLVM_ADT_MAPVECTOR_H
|
||||
+
|
||||
+#include "llvm/ADT/ArrayRef.h"
|
||||
+#include "llvm/ADT/DenseMap.h"
|
||||
+#include <vector>
|
||||
+
|
||||
+namespace llvm {
|
||||
+
|
||||
+/// This class implements a map that also provides access to all stored values
|
||||
+/// in a deterministic order. The values are kept in a std::vector and the
|
||||
+/// mapping is done with DenseMap from Keys to indexes in that vector.
|
||||
+template<typename KeyT, typename ValueT>
|
||||
+class MapVector {
|
||||
+ typedef llvm::DenseMap<KeyT, unsigned> MapType;
|
||||
+ typedef std::vector<std::pair<KeyT, ValueT> > VectorType;
|
||||
+ typedef typename VectorType::size_type SizeType;
|
||||
+
|
||||
+ MapType Map;
|
||||
+ VectorType Vector;
|
||||
+
|
||||
+public:
|
||||
+ typedef typename VectorType::iterator iterator;
|
||||
+ typedef typename VectorType::const_iterator const_iterator;
|
||||
+
|
||||
+ SizeType size() const {
|
||||
+ return Vector.size();
|
||||
+ }
|
||||
+
|
||||
+ iterator begin() {
|
||||
+ return Vector.begin();
|
||||
+ }
|
||||
+
|
||||
+ const_iterator begin() const {
|
||||
+ return Vector.begin();
|
||||
+ }
|
||||
+
|
||||
+ iterator end() {
|
||||
+ return Vector.end();
|
||||
+ }
|
||||
+
|
||||
+ const_iterator end() const {
|
||||
+ return Vector.end();
|
||||
+ }
|
||||
+
|
||||
+ bool empty() const {
|
||||
+ return Vector.empty();
|
||||
+ }
|
||||
+
|
||||
+ ValueT &operator[](const KeyT &Key) {
|
||||
+ std::pair<KeyT, unsigned> Pair = std::make_pair(Key, 0);
|
||||
+ std::pair<typename MapType::iterator, bool> Result = Map.insert(Pair);
|
||||
+ unsigned &I = Result.first->second;
|
||||
+ if (Result.second) {
|
||||
+ Vector.push_back(std::make_pair(Key, ValueT()));
|
||||
+ I = Vector.size() - 1;
|
||||
+ }
|
||||
+ return Vector[I].second;
|
||||
+ }
|
||||
+};
|
||||
+
|
||||
+}
|
||||
+
|
||||
+#endif
|
|
@ -3853,7 +3853,7 @@ MOZ_ARG_WITH_BOOL(system-nspr,
|
|||
_USE_SYSTEM_NSPR=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSPR"; then
|
||||
AM_PATH_NSPR(4.9.2, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
AM_PATH_NSPR(4.9.3, [MOZ_NATIVE_NSPR=1], [AC_MSG_ERROR([your don't have NSPR installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSPR"; then
|
||||
|
@ -3934,7 +3934,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
|
|||
_USE_SYSTEM_NSS=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSS"; then
|
||||
AM_PATH_NSS(3.13.2, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
AM_PATH_NSS(3.14, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSS"; then
|
||||
|
|
|
@ -79,8 +79,8 @@ class Element;
|
|||
} // namespace mozilla
|
||||
|
||||
#define NS_IDOCUMENT_IID \
|
||||
{ 0x57fe44ae, 0x6656, 0x44b8, \
|
||||
{ 0x8d, 0xc0, 0xfc, 0xa7, 0x43, 0x28, 0xbe, 0x86 } }
|
||||
{ 0x0e1324c9, 0xc997, 0x447e, \
|
||||
{ 0xbc, 0xd9, 0xa6, 0x57, 0x80, 0x29, 0x91, 0xe4 } }
|
||||
|
||||
// Flag for AddStyleSheet().
|
||||
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
||||
|
@ -1621,7 +1621,10 @@ public:
|
|||
|
||||
// Add/Remove images from the document image tracker
|
||||
virtual nsresult AddImage(imgIRequest* aImage) = 0;
|
||||
virtual nsresult RemoveImage(imgIRequest* aImage) = 0;
|
||||
// If the REQUEST_DISCARD flag is passed then if the lock count is zero we
|
||||
// will request the image be discarded now (instead of waiting).
|
||||
enum { REQUEST_DISCARD = 0x1 };
|
||||
virtual nsresult RemoveImage(imgIRequest* aImage, uint32_t aFlags = 0) = 0;
|
||||
|
||||
// Makes the images on this document locked/unlocked. By default, the locking
|
||||
// state is unlocked/false.
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -20,17 +21,16 @@ XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
|||
|
||||
// Module stuff
|
||||
var EXPORTED_SYMBOLS = ["CSPRep", "CSPSourceList", "CSPSource", "CSPHost",
|
||||
"CSPWarning", "CSPError", "CSPdebug",
|
||||
"CSPViolationReportListener", "CSPLocalizer"];
|
||||
"CSPdebug", "CSPViolationReportListener", "CSPLocalizer"];
|
||||
|
||||
var STRINGS_URI = "chrome://global/locale/security/csp.properties";
|
||||
|
||||
// these are not exported
|
||||
var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService);
|
||||
.getService(Ci.nsIIOService);
|
||||
|
||||
var gETLDService = Components.classes["@mozilla.org/network/effective-tld-service;1"]
|
||||
.getService(Components.interfaces.nsIEffectiveTLDService);
|
||||
.getService(Ci.nsIEffectiveTLDService);
|
||||
|
||||
// These regexps represent the concrete syntax on the w3 spec as of 7-5-2012
|
||||
// scheme = <scheme production from RFC 3986>
|
||||
|
@ -76,58 +76,30 @@ var gPrefObserver = {
|
|||
|
||||
_initialize: function() {
|
||||
var prefSvc = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefService);
|
||||
.getService(Ci.nsIPrefService);
|
||||
this._branch = prefSvc.getBranch("security.csp.");
|
||||
this._branch.addObserver("", this, false);
|
||||
this._debugEnabled = this._branch.getBoolPref("debug");
|
||||
},
|
||||
|
||||
unregister: function() {
|
||||
if(!this._branch) return;
|
||||
if (!this._branch) return;
|
||||
this._branch.removeObserver("", this);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if(aTopic != "nsPref:changed") return;
|
||||
if(aData === "debug")
|
||||
if (aTopic != "nsPref:changed") return;
|
||||
if (aData === "debug")
|
||||
this._debugEnabled = this._branch.getBoolPref("debug");
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
function CSPWarning(aMsg, aWindowID, aSource, aScriptSample, aLineNum) {
|
||||
var textMessage = 'CSP WARN: ' + aMsg + "\n";
|
||||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptError);
|
||||
consoleMsg.initWithWindowID(textMessage, aSource, aScriptSample, aLineNum, 0,
|
||||
Components.interfaces.nsIScriptError.warningFlag,
|
||||
"Content Security Policy", aWindowID);
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Components.interfaces.nsIConsoleService)
|
||||
.logMessage(consoleMsg);
|
||||
}
|
||||
|
||||
function CSPError(aMsg, aWindowID) {
|
||||
var textMessage = 'CSP ERROR: ' + aMsg + "\n";
|
||||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptError);
|
||||
consoleMsg.initWithWindowID(textMessage, null, null, 0, 0,
|
||||
Components.interfaces.nsIScriptError.errorFlag,
|
||||
"Content Security Policy", aWindowID);
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Components.interfaces.nsIConsoleService)
|
||||
.logMessage(consoleMsg);
|
||||
}
|
||||
|
||||
function CSPdebug(aMsg) {
|
||||
if (!gPrefObserver.debugEnabled) return;
|
||||
|
||||
aMsg = 'CSP debug: ' + aMsg + "\n";
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Components.interfaces.nsIConsoleService)
|
||||
.getService(Ci.nsIConsoleService)
|
||||
.logStringMessage(aMsg);
|
||||
}
|
||||
|
||||
|
@ -138,16 +110,16 @@ function CSPPolicyURIListener(policyURI, docRequest, csp) {
|
|||
this._csp = csp; // parent document's CSP
|
||||
this._policy = ""; // contents fetched from policyURI
|
||||
this._wrapper = null; // nsIScriptableInputStream
|
||||
this._docURI = docRequest.QueryInterface(Components.interfaces.nsIChannel)
|
||||
this._docURI = docRequest.QueryInterface(Ci.nsIChannel)
|
||||
.URI; // parent document URI (to be used as 'self')
|
||||
}
|
||||
|
||||
CSPPolicyURIListener.prototype = {
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if (iid.equals(Components.interfaces.nsIStreamListener) ||
|
||||
iid.equals(Components.interfaces.nsIRequestObserver) ||
|
||||
iid.equals(Components.interfaces.nsISupports))
|
||||
if (iid.equals(Ci.nsIStreamListener) ||
|
||||
iid.equals(Ci.nsIRequestObserver) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
},
|
||||
|
@ -159,7 +131,7 @@ CSPPolicyURIListener.prototype = {
|
|||
function(request, context, inputStream, offset, count) {
|
||||
if (this._wrapper == null) {
|
||||
this._wrapper = Components.classes["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
this._wrapper.init(inputStream);
|
||||
}
|
||||
// store the remote policy as it becomes available
|
||||
|
@ -241,9 +213,10 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
var UD = CSPRep.URI_DIRECTIVES;
|
||||
var aCSPR = new CSPRep();
|
||||
aCSPR._originalText = aStr;
|
||||
aCSPR._innerWindowID = innerWindowFromRequest(docRequest);
|
||||
|
||||
var selfUri = null;
|
||||
if (self instanceof Components.interfaces.nsIURI)
|
||||
if (self instanceof Ci.nsIURI)
|
||||
selfUri = self.clone();
|
||||
|
||||
var dirs = aStr.split(";");
|
||||
|
@ -258,7 +231,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
|
||||
if (aCSPR._directives.hasOwnProperty(dirname)) {
|
||||
// Check for (most) duplicate directives
|
||||
CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
|
||||
[dirname]));
|
||||
CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
|
||||
continue directive;
|
||||
}
|
||||
|
@ -267,7 +241,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
if (dirname === CSPRep.OPTIONS_DIRECTIVE) {
|
||||
if (aCSPR._allowInlineScripts || aCSPR._allowEval) {
|
||||
// Check for duplicate options directives
|
||||
CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
|
||||
[dirname]));
|
||||
CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
|
||||
continue directive;
|
||||
}
|
||||
|
@ -280,7 +255,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
else if (opt === "eval-script")
|
||||
aCSPR._allowEval = true;
|
||||
else
|
||||
CSPWarning(CSPLocalizer.getFormatStr("doNotUnderstandOption", [opt]));
|
||||
cspWarn(aCSPR, CSPLocalizer.getFormatStr("doNotUnderstandOption",
|
||||
[opt]));
|
||||
}
|
||||
continue directive;
|
||||
}
|
||||
|
@ -289,14 +265,15 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
// parse "allow" as equivalent to "default-src", at least until the spec
|
||||
// stabilizes, at which time we can stop parsing "allow"
|
||||
if (dirname === CSPRep.ALLOW_DIRECTIVE) {
|
||||
CSPWarning(CSPLocalizer.getStr("allowDirectiveDeprecated"));
|
||||
cspWarn(aCSPR, CSPLocalizer.getStr("allowDirectiveDeprecated"));
|
||||
if (aCSPR._directives.hasOwnProperty(SD.DEFAULT_SRC)) {
|
||||
// Check for duplicate default-src and allow directives
|
||||
CSPError(CSPLocalizer.getFormatStr("duplicateDirective", [dirname]));
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("duplicateDirective",
|
||||
[dirname]));
|
||||
CSPdebug("Skipping duplicate directive: \"" + dir + "\"");
|
||||
continue directive;
|
||||
}
|
||||
var dv = CSPSourceList.fromString(dirvalue, self, true);
|
||||
var dv = CSPSourceList.fromString(dirvalue, aCSPR, self, true);
|
||||
if (dv) {
|
||||
aCSPR._directives[SD.DEFAULT_SRC] = dv;
|
||||
continue directive;
|
||||
|
@ -307,7 +284,7 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
for each(var sdi in SD) {
|
||||
if (dirname === sdi) {
|
||||
// process dirs, and enforce that 'self' is defined.
|
||||
var dv = CSPSourceList.fromString(dirvalue, self, true);
|
||||
var dv = CSPSourceList.fromString(dirvalue, aCSPR, self, true);
|
||||
if (dv) {
|
||||
aCSPR._directives[sdi] = dv;
|
||||
continue directive;
|
||||
|
@ -340,18 +317,19 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
if (self) {
|
||||
if (gETLDService.getBaseDomain(uri) !==
|
||||
gETLDService.getBaseDomain(selfUri)) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("notETLDPlus1",
|
||||
[gETLDService.getBaseDomain(uri)]));
|
||||
cspWarn(aCSPR,
|
||||
CSPLocalizer.getFormatStr("notETLDPlus1",
|
||||
[gETLDService.getBaseDomain(uri)]));
|
||||
continue;
|
||||
}
|
||||
if (!uri.schemeIs(selfUri.scheme)) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("notSameScheme",
|
||||
[uri.asciiSpec]));
|
||||
cspWarn(aCSPR, CSPLocalizer.getFormatStr("notSameScheme",
|
||||
[uri.asciiSpec]));
|
||||
continue;
|
||||
}
|
||||
if (uri.port && uri.port !== selfUri.port) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("notSamePort",
|
||||
[uri.asciiSpec]));
|
||||
cspWarn(aCSPR, CSPLocalizer.getFormatStr("notSamePort",
|
||||
[uri.asciiSpec]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -360,15 +338,16 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
|
||||
case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
|
||||
if (uri.host !== selfUri.host) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
|
||||
[selfUri.host, uri.host]));
|
||||
cspWarn(aCSPR,
|
||||
CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
|
||||
[selfUri.host, uri.host]));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
CSPWarning(CSPLocalizer.getFormatStr("couldNotParseReportURI",
|
||||
[uriStrings[i]]));
|
||||
cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotParseReportURI",
|
||||
[uriStrings[i]]));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -383,13 +362,13 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
if (dirname === UD.POLICY_URI) {
|
||||
// POLICY_URI can only be alone
|
||||
if (aCSPR._directives.length > 0 || dirs.length > 1) {
|
||||
CSPError(CSPLocalizer.getStr("policyURINotAlone"));
|
||||
cspError(aCSPR, CSPLocalizer.getStr("policyURINotAlone"));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
// if we were called without a reference to the parent document request
|
||||
// we won't be able to suspend it while we fetch the policy -> fail closed
|
||||
if (!docRequest || !csp) {
|
||||
CSPError(CSPLocalizer.getStr("noParentRequest"));
|
||||
cspError(aCSPR, CSPLocalizer.getStr("noParentRequest"));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
|
@ -397,22 +376,26 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
try {
|
||||
uri = gIoService.newURI(dirvalue, null, selfUri);
|
||||
} catch(e) {
|
||||
CSPError(CSPLocalizer.getFormatStr("policyURIParseError", [dirvalue]));
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("policyURIParseError",
|
||||
[dirvalue]));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
// Verify that policy URI comes from the same origin
|
||||
if (selfUri) {
|
||||
if (selfUri.host !== uri.host){
|
||||
CSPError(CSPLocalizer.getFormatStr("nonMatchingHost", [uri.host]));
|
||||
if (selfUri.host !== uri.host) {
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingHost",
|
||||
[uri.host]));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
if (selfUri.port !== uri.port){
|
||||
CSPError(CSPLocalizer.getFormatStr("nonMatchingPort", [uri.port.toString()]));
|
||||
if (selfUri.port !== uri.port) {
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingPort",
|
||||
[uri.port.toString()]));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
if (selfUri.scheme !== uri.scheme){
|
||||
CSPError(CSPLocalizer.getFormatStr("nonMatchingScheme", [uri.scheme]));
|
||||
if (selfUri.scheme !== uri.scheme) {
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("nonMatchingScheme",
|
||||
[uri.scheme]));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
}
|
||||
|
@ -423,13 +406,14 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
var chan = gIoService.newChannel(uri.asciiSpec, null, null);
|
||||
// make request anonymous (no cookies, etc.) so the request for the
|
||||
// policy-uri can't be abused for CSRF
|
||||
chan.loadFlags |= Components.interfaces.nsIChannel.LOAD_ANONYMOUS;
|
||||
chan.loadFlags |= Ci.nsIChannel.LOAD_ANONYMOUS;
|
||||
chan.asyncOpen(new CSPPolicyURIListener(uri, docRequest, csp), null);
|
||||
}
|
||||
catch (e) {
|
||||
// resume the document request and apply most restrictive policy
|
||||
docRequest.resume();
|
||||
CSPError(CSPLocalizer.getFormatStr("errorFetchingPolicy", [e.toString()]));
|
||||
cspError(aCSPR, CSPLocalizer.getFormatStr("errorFetchingPolicy",
|
||||
[e.toString()]));
|
||||
return CSPRep.fromString("default-src 'none'");
|
||||
}
|
||||
|
||||
|
@ -439,7 +423,8 @@ CSPRep.fromString = function(aStr, self, docRequest, csp) {
|
|||
}
|
||||
|
||||
// UNIDENTIFIED DIRECTIVE /////////////////////////////////////////////
|
||||
CSPWarning(CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective", [dirname]));
|
||||
cspWarn(aCSPR, CSPLocalizer.getFormatStr("couldNotProcessUnknownDirective",
|
||||
[dirname]));
|
||||
|
||||
} // end directive: loop
|
||||
|
||||
|
@ -511,7 +496,7 @@ CSPRep.prototype = {
|
|||
// GLOBALLY ALLOW "about:" SCHEME
|
||||
if (aURI instanceof String && aURI.substring(0,6) === "about:")
|
||||
return true;
|
||||
if (aURI instanceof Components.interfaces.nsIURI && aURI.scheme === "about")
|
||||
if (aURI instanceof Ci.nsIURI && aURI.scheme === "about")
|
||||
return true;
|
||||
|
||||
// make sure the context is valid
|
||||
|
@ -563,6 +548,9 @@ CSPRep.prototype = {
|
|||
|
||||
newRep._allowInlineScripts = this.allowsInlineScripts
|
||||
&& aCSPRep.allowsInlineScripts;
|
||||
|
||||
newRep._innerWindowID = this._innerWindowID ?
|
||||
this._innerWindowID : aCSPRep._innerWindowID;
|
||||
|
||||
return newRep;
|
||||
},
|
||||
|
@ -578,7 +566,7 @@ CSPRep.prototype = {
|
|||
var SD = CSPRep.SRC_DIRECTIVES;
|
||||
var defaultSrcDir = this._directives[SD.DEFAULT_SRC];
|
||||
if (!defaultSrcDir) {
|
||||
CSPWarning(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
|
||||
this.warn(CSPLocalizer.getStr("allowOrDefaultSrcRequired"));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -589,7 +577,7 @@ CSPRep.prototype = {
|
|||
// implicit directive, make explicit.
|
||||
// All but frame-ancestors directive inherit from 'allow' (bug 555068)
|
||||
if (dirv === SD.FRAME_ANCESTORS)
|
||||
this._directives[dirv] = CSPSourceList.fromString("*");
|
||||
this._directives[dirv] = CSPSourceList.fromString("*",this);
|
||||
else
|
||||
this._directives[dirv] = defaultSrcDir.clone();
|
||||
this._directives[dirv]._isImplicit = true;
|
||||
|
@ -613,6 +601,62 @@ CSPRep.prototype = {
|
|||
get allowsInlineScripts () {
|
||||
return this._allowInlineScripts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a warning message to the error console and web developer console.
|
||||
* @param aMsg
|
||||
* The message to send
|
||||
* @param aSource (optional)
|
||||
* The URL of the file in which the error occurred
|
||||
* @param aScriptLine (optional)
|
||||
* The line in the source file which the error occurred
|
||||
* @param aLineNum (optional)
|
||||
* The number of the line where the error occurred
|
||||
*/
|
||||
warn:
|
||||
function cspd_warn(aMsg, aSource, aScriptLine, aLineNum) {
|
||||
var textMessage = 'CSP WARN: ' + aMsg + "\n";
|
||||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Ci.nsIScriptError);
|
||||
if (this._innerWindowID) {
|
||||
consoleMsg.initWithWindowID(textMessage, aSource, aScriptLine, aLineNum,
|
||||
0, Ci.nsIScriptError.warningFlag,
|
||||
"Content Security Policy",
|
||||
this._innerWindowID);
|
||||
} else {
|
||||
consoleMsg.init(textMessage, aSource, aScriptLine, aLineNum, 0,
|
||||
Ci.nsIScriptError.warningFlag,
|
||||
"Content Security Policy");
|
||||
}
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Ci.nsIConsoleService).logMessage(consoleMsg);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends an error message to the error console and web developer console.
|
||||
* @param aMsg
|
||||
* The message to send
|
||||
*/
|
||||
error:
|
||||
function cspsd_error(aMsg) {
|
||||
var textMessage = 'CSP ERROR: ' + aMsg + "\n";
|
||||
|
||||
var consoleMsg = Components.classes["@mozilla.org/scripterror;1"]
|
||||
.createInstance(Ci.nsIScriptError);
|
||||
if (this._innerWindowID) {
|
||||
consoleMsg.initWithWindowID(textMessage, null, null, 0, 0,
|
||||
Ci.nsIScriptError.errorFlag,
|
||||
"Content Security Policy",
|
||||
this._innerWindowID);
|
||||
}
|
||||
else {
|
||||
consoleMsg.init(textMessage, null, null, 0, 0,
|
||||
Ci.nsIScriptError.errorFlag, "Content Security Policy");
|
||||
}
|
||||
Components.classes["@mozilla.org/consoleservice;1"]
|
||||
.getService(Ci.nsIConsoleService).logMessage(consoleMsg);
|
||||
},
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -633,6 +677,9 @@ function CSPSourceList() {
|
|||
*
|
||||
* @param aStr
|
||||
* string rep of a CSP Source List
|
||||
* @param aCSPRep
|
||||
* the CSPRep to which this souce list belongs. If null, CSP errors and
|
||||
* warnings will not be sent to the web console.
|
||||
* @param self (optional)
|
||||
* URI or CSPSource representing the "self" source
|
||||
* @param enforceSelfChecks (optional)
|
||||
|
@ -641,39 +688,42 @@ function CSPSourceList() {
|
|||
* @returns
|
||||
* an instance of CSPSourceList
|
||||
*/
|
||||
CSPSourceList.fromString = function(aStr, self, enforceSelfChecks) {
|
||||
CSPSourceList.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
|
||||
// source-list = *WSP [ source-expression *( 1*WSP source-expression ) *WSP ]
|
||||
// / *WSP "'none'" *WSP
|
||||
|
||||
/* If self parameter is passed, convert to CSPSource,
|
||||
unless it is already a CSPSource. */
|
||||
if(self && !(self instanceof CSPSource)) {
|
||||
self = CSPSource.create(self);
|
||||
if (self && !(self instanceof CSPSource)) {
|
||||
self = CSPSource.create(self, aCSPRep);
|
||||
}
|
||||
|
||||
var slObj = new CSPSourceList();
|
||||
slObj._CSPRep = aCSPRep;
|
||||
aStr = aStr.trim();
|
||||
// w3 specifies case insensitive equality
|
||||
if (aStr.toUpperCase() === "'NONE'"){
|
||||
if (aStr.toUpperCase() === "'NONE'") {
|
||||
slObj._permitAllSources = false;
|
||||
return slObj;
|
||||
}
|
||||
|
||||
var tokens = aStr.split(/\s+/);
|
||||
for (var i in tokens) {
|
||||
if (!R_SOURCEEXP.test(tokens[i])){
|
||||
CSPWarning(CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
|
||||
[tokens[i]]));
|
||||
if (!R_SOURCEEXP.test(tokens[i])) {
|
||||
cspWarn(aCSPRep,
|
||||
CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
|
||||
[tokens[i]]));
|
||||
continue;
|
||||
}
|
||||
var src = CSPSource.create(tokens[i], self, enforceSelfChecks);
|
||||
var src = CSPSource.create(tokens[i], aCSPRep, self, enforceSelfChecks);
|
||||
if (!src) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
|
||||
[tokens[i]]));
|
||||
cspWarn(aCSPRep,
|
||||
CSPLocalizer.getFormatStr("failedToParseUnrecognizedSource",
|
||||
[tokens[i]]));
|
||||
continue;
|
||||
}
|
||||
// if a source is a *, then we can permit all sources
|
||||
if (src.permitAll){
|
||||
if (src.permitAll) {
|
||||
slObj._permitAllSources = true;
|
||||
return slObj;
|
||||
} else {
|
||||
|
@ -758,6 +808,7 @@ CSPSourceList.prototype = {
|
|||
function() {
|
||||
var aSL = new CSPSourceList();
|
||||
aSL._permitAllSources = this._permitAllSources;
|
||||
aSL._CSPRep = this._CSPRep;
|
||||
for (var i in this._sources) {
|
||||
aSL._sources[i] = this._sources[i].clone();
|
||||
}
|
||||
|
@ -800,7 +851,7 @@ CSPSourceList.prototype = {
|
|||
if (!that) return this.clone();
|
||||
|
||||
if (this.isNone() || that.isNone())
|
||||
newCSPSrcList = CSPSourceList.fromString("'none'");
|
||||
newCSPSrcList = CSPSourceList.fromString("'none'", this._CSPRep);
|
||||
|
||||
if (this.isAll()) newCSPSrcList = that.clone();
|
||||
if (that.isAll()) newCSPSrcList = this.clone();
|
||||
|
@ -837,6 +888,9 @@ CSPSourceList.prototype = {
|
|||
// if either was explicit, so is this.
|
||||
newCSPSrcList._isImplicit = this._isImplicit && that._isImplicit;
|
||||
|
||||
if ((!newCSPSrcList._CSPRep) && that._CSPRep) {
|
||||
newCSPSrcList._CSPRep = that._CSPRep;
|
||||
}
|
||||
return newCSPSrcList;
|
||||
}
|
||||
}
|
||||
|
@ -865,6 +919,9 @@ function CSPSource() {
|
|||
* - CSPSource (clone)
|
||||
* @param aData
|
||||
* string, nsURI, or CSPSource
|
||||
* @param aCSPRep
|
||||
* The CSPRep this source belongs to. If null, CSP errors and warnings
|
||||
* will not be sent to the web console.
|
||||
* @param self (optional)
|
||||
* if present, string, URI, or CSPSource representing the "self" resource
|
||||
* @param enforceSelfChecks (optional)
|
||||
|
@ -873,12 +930,12 @@ function CSPSource() {
|
|||
* @returns
|
||||
* an instance of CSPSource
|
||||
*/
|
||||
CSPSource.create = function(aData, self, enforceSelfChecks) {
|
||||
CSPSource.create = function(aData, aCSPRep, self, enforceSelfChecks) {
|
||||
if (typeof aData === 'string')
|
||||
return CSPSource.fromString(aData, self, enforceSelfChecks);
|
||||
return CSPSource.fromString(aData, aCSPRep, self, enforceSelfChecks);
|
||||
|
||||
if (aData instanceof Components.interfaces.nsIURI)
|
||||
return CSPSource.fromURI(aData, self, enforceSelfChecks);
|
||||
if (aData instanceof Ci.nsIURI)
|
||||
return CSPSource.fromURI(aData, aCSPRep, self, enforceSelfChecks);
|
||||
|
||||
if (aData instanceof CSPSource) {
|
||||
var ns = aData.clone();
|
||||
|
@ -896,6 +953,9 @@ CSPSource.create = function(aData, self, enforceSelfChecks) {
|
|||
*
|
||||
* @param aURI
|
||||
* nsIURI rep of a URI
|
||||
* @param aCSPRep
|
||||
* The policy this source belongs to. If null, CSP errors and warnings
|
||||
* will not be sent to the web console.
|
||||
* @param self (optional)
|
||||
* string or CSPSource representing the "self" source
|
||||
* @param enforceSelfChecks (optional)
|
||||
|
@ -904,23 +964,24 @@ CSPSource.create = function(aData, self, enforceSelfChecks) {
|
|||
* @returns
|
||||
* an instance of CSPSource
|
||||
*/
|
||||
CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
|
||||
if (!(aURI instanceof Components.interfaces.nsIURI)){
|
||||
CSPError(CSPLocalizer.getStr("cspSourceNotURI"));
|
||||
CSPSource.fromURI = function(aURI, aCSPRep, self, enforceSelfChecks) {
|
||||
if (!(aURI instanceof Ci.nsIURI)) {
|
||||
cspError(aCSPRep, CSPLocalizer.getStr("cspSourceNotURI"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!self && enforceSelfChecks) {
|
||||
CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
|
||||
cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self && !(self instanceof CSPSource)) {
|
||||
self = CSPSource.create(self, undefined, false);
|
||||
self = CSPSource.create(self, aCSPRep, undefined, false);
|
||||
}
|
||||
|
||||
var sObj = new CSPSource();
|
||||
sObj._self = self;
|
||||
sObj._CSPRep = aCSPRep;
|
||||
|
||||
// PARSE
|
||||
// If 'self' is undefined, then use default port for scheme if there is one.
|
||||
|
@ -930,7 +991,8 @@ CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
|
|||
sObj._scheme = aURI.scheme;
|
||||
} catch(e) {
|
||||
sObj._scheme = undefined;
|
||||
CSPError(CSPLocalizer.getFormatStr("uriWithoutScheme", [aURI.asciiSpec]));
|
||||
cspError(aCSPRep, CSPLocalizer.getFormatStr("uriWithoutScheme",
|
||||
[aURI.asciiSpec]));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -973,6 +1035,8 @@ CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
|
|||
*
|
||||
* @param aStr
|
||||
* string rep of a CSP Source
|
||||
* @param aCSPRep
|
||||
* the CSPRep this CSPSource belongs to
|
||||
* @param self (optional)
|
||||
* string, URI, or CSPSource representing the "self" source
|
||||
* @param enforceSelfChecks (optional)
|
||||
|
@ -981,35 +1045,37 @@ CSPSource.fromURI = function(aURI, self, enforceSelfChecks) {
|
|||
* @returns
|
||||
* an instance of CSPSource
|
||||
*/
|
||||
CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
|
||||
CSPSource.fromString = function(aStr, aCSPRep, self, enforceSelfChecks) {
|
||||
if (!aStr)
|
||||
return null;
|
||||
|
||||
if (!(typeof aStr === 'string')) {
|
||||
CSPError(CSPLocalizer.getStr("argumentIsNotString"));
|
||||
cspError(aCSPRep, CSPLocalizer.getStr("argumentIsNotString"));
|
||||
return null;
|
||||
}
|
||||
|
||||
var sObj = new CSPSource();
|
||||
sObj._self = self;
|
||||
sObj._CSPRep = aCSPRep;
|
||||
|
||||
|
||||
// if equal, return does match
|
||||
if (aStr === "*"){
|
||||
if (aStr === "*") {
|
||||
sObj._permitAll = true;
|
||||
return sObj;
|
||||
}
|
||||
|
||||
if (!self && enforceSelfChecks) {
|
||||
CSPError(CSPLocalizer.getStr("selfDataNotProvided"));
|
||||
cspError(aCSPRep, CSPLocalizer.getStr("selfDataNotProvided"));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (self && !(self instanceof CSPSource)) {
|
||||
self = CSPSource.create(self, undefined, false);
|
||||
self = CSPSource.create(self, aCSPRep, undefined, false);
|
||||
}
|
||||
|
||||
// check for scheme-source match
|
||||
if (R_SCHEMESRC.test(aStr)){
|
||||
if (R_SCHEMESRC.test(aStr)) {
|
||||
var schemeSrcMatch = R_GETSCHEME.exec(aStr);
|
||||
sObj._scheme = schemeSrcMatch[0];
|
||||
if (!sObj._host) sObj._host = CSPHost.fromString("*");
|
||||
|
@ -1031,8 +1097,9 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
|
|||
|
||||
// get array of matches to the R_HOST regular expression
|
||||
var hostMatch = R_HOST.exec(aStr);
|
||||
if (!hostMatch){
|
||||
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource", [aStr]));
|
||||
if (!hostMatch) {
|
||||
cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
|
||||
[aStr]));
|
||||
return null;
|
||||
}
|
||||
// host regex gets scheme, so remove scheme from aStr. Add 3 for '://'
|
||||
|
@ -1044,7 +1111,9 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
|
|||
// gets the default port for the given scheme
|
||||
defPort = Services.io.getProtocolHandler(sObj._scheme).defaultPort;
|
||||
if (!defPort) {
|
||||
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource", [aStr]));
|
||||
cspError(aCSPRep,
|
||||
CSPLocalizer.getFormatStr("couldntParseInvalidSource",
|
||||
[aStr]));
|
||||
return null;
|
||||
}
|
||||
sObj._port = defPort;
|
||||
|
@ -1057,16 +1126,17 @@ CSPSource.fromString = function(aStr, self, enforceSelfChecks) {
|
|||
}
|
||||
|
||||
// check for 'self' (case insensitive)
|
||||
if (aStr.toUpperCase() === "'SELF'"){
|
||||
if (!self){
|
||||
CSPError(CSPLocalizer.getStr("selfKeywordNoSelfData"));
|
||||
if (aStr.toUpperCase() === "'SELF'") {
|
||||
if (!self) {
|
||||
cspError(aCSPRep, CSPLocalizer.getStr("selfKeywordNoSelfData"));
|
||||
return null;
|
||||
}
|
||||
sObj._self = self.clone();
|
||||
sObj._isSelf = true;
|
||||
return sObj;
|
||||
}
|
||||
CSPError(CSPLocalizer.getFormatStr("couldntParseInvalidSource",[aStr]));
|
||||
cspError(aCSPRep, CSPLocalizer.getFormatStr("couldntParseInvalidSource",
|
||||
[aStr]));
|
||||
return null;
|
||||
};
|
||||
|
||||
|
@ -1156,6 +1226,7 @@ CSPSource.prototype = {
|
|||
aClone._port = this._port;
|
||||
aClone._host = this._host ? this._host.clone() : undefined;
|
||||
aClone._isSelf = this._isSelf;
|
||||
aClone._CSPRep = this._CSPRep;
|
||||
return aClone;
|
||||
},
|
||||
|
||||
|
@ -1171,7 +1242,7 @@ CSPSource.prototype = {
|
|||
if (!aSource) return false;
|
||||
|
||||
if (!(aSource instanceof CSPSource))
|
||||
return this.permits(CSPSource.create(aSource));
|
||||
return this.permits(CSPSource.create(aSource, this._CSPRep));
|
||||
|
||||
// verify scheme
|
||||
if (this.scheme != aSource.scheme)
|
||||
|
@ -1225,7 +1296,9 @@ CSPSource.prototype = {
|
|||
else if (that.port === this.port)
|
||||
newSource._port = this.port;
|
||||
else {
|
||||
CSPError(CSPLocalizer.getFormatStr("notIntersectPort", [this.toString(), that.toString()]));
|
||||
let msg = CSPLocalizer.getFormatStr("notIntersectPort",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1241,7 +1314,9 @@ CSPSource.prototype = {
|
|||
else if (that.scheme === this.scheme)
|
||||
newSource._scheme = this.scheme;
|
||||
else {
|
||||
CSPError(CSPLocalizer.getFormatStr("notIntersectScheme", [this.toString(), that.toString()]));
|
||||
var msg = CSPLocalizer.getFormatStr("notIntersectScheme",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -1257,13 +1332,19 @@ CSPSource.prototype = {
|
|||
if (this.host && that.host) {
|
||||
newSource._host = this.host.intersectWith(that.host);
|
||||
} else if (this.host) {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [that.toString()]));
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
|
||||
[that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = this.host.clone();
|
||||
} else if (that.host) {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost", [this.toString()]));
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourceWithUndefinedHost",
|
||||
[this.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = that.host.clone();
|
||||
} else {
|
||||
CSPError(CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts", [this.toString(), that.toString()]));
|
||||
let msg = CSPLocalizer.getFormatStr("intersectingSourcesWithUndefinedHosts",
|
||||
[this.toString(), that.toString()]);
|
||||
cspError(this._CSPRep, msg);
|
||||
newSource._host = CSPHost.fromString("*");
|
||||
}
|
||||
|
||||
|
@ -1482,7 +1563,7 @@ CSPViolationReportListener.prototype = {
|
|||
_reportURI: null,
|
||||
|
||||
QueryInterface: function(iid) {
|
||||
if(iid.equals(Ci.nsIStreamListener) ||
|
||||
if (iid.equals(Ci.nsIStreamListener) ||
|
||||
iid.equals(Ci.nsIRequestObserver) ||
|
||||
iid.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
@ -1508,6 +1589,52 @@ CSPViolationReportListener.prototype = {
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
function innerWindowFromRequest(docRequest) {
|
||||
let win = null;
|
||||
let loadContext = null;
|
||||
|
||||
try {
|
||||
loadContext = docRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||
} catch (ex) {
|
||||
try {
|
||||
loadContext = docRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (loadContext) {
|
||||
win = loadContext.associatedWindow;
|
||||
}
|
||||
if (win) {
|
||||
try {
|
||||
let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
return winUtils.currentInnerWindowID;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function cspError(aCSPRep, aMessage) {
|
||||
if (aCSPRep) {
|
||||
aCSPRep.error(aMessage);
|
||||
} else {
|
||||
(new CSPRep()).error(aMessage);
|
||||
}
|
||||
}
|
||||
|
||||
function cspWarn(aCSPRep, aMessage) {
|
||||
if (aCSPRep) {
|
||||
aCSPRep.warn(aMessage);
|
||||
} else {
|
||||
(new CSPRep()).warn(aMessage);
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
CSPLocalizer = {
|
||||
/**
|
||||
* Retrieve a localized string.
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
#include "nsRuleProcessorData.h"
|
||||
#include "nsAsyncDOMEvent.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "mozilla/dom/NodeListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
|
@ -389,7 +390,13 @@ JSObject*
|
|||
nsChildContentList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
|
||||
JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::NodeList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -104,34 +104,6 @@ ContentSecurityPolicy.prototype = {
|
|||
return this._reportOnlyMode || this._policy.allowsEvalInScripts;
|
||||
},
|
||||
|
||||
get innerWindowID() {
|
||||
let win = null;
|
||||
let loadContext = null;
|
||||
|
||||
try {
|
||||
loadContext = this._docRequest
|
||||
.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||
} catch (ex) {
|
||||
try {
|
||||
loadContext = this._docRequest.loadGroup
|
||||
.notificationCallbacks.getInterface(Ci.nsILoadContext);
|
||||
} catch (ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (loadContext) {
|
||||
win = loadContext.associatedWindow;
|
||||
}
|
||||
if (win) {
|
||||
try {
|
||||
let winUtils = win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
return winUtils.currentInnerWindowID;
|
||||
} catch (ex) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
|
@ -302,16 +274,15 @@ ContentSecurityPolicy.prototype = {
|
|||
CSPdebug("Constructed violation report:\n" + reportString);
|
||||
|
||||
var violationMessage = null;
|
||||
if(blockedUri["asciiSpec"]){
|
||||
if (blockedUri["asciiSpec"]) {
|
||||
violationMessage = CSPLocalizer.getFormatStr("directiveViolatedWithURI", [violatedDirective, blockedUri.asciiSpec]);
|
||||
} else {
|
||||
violationMessage = CSPLocalizer.getFormatStr("directiveViolated", [violatedDirective]);
|
||||
}
|
||||
CSPWarning(violationMessage,
|
||||
this.innerWindowID,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
this._policy.warn(violationMessage,
|
||||
(aSourceFile) ? aSourceFile : null,
|
||||
(aScriptSample) ? decodeURIComponent(aScriptSample) : null,
|
||||
(aLineNum) ? aLineNum : null);
|
||||
|
||||
// For each URI in the report list, send out a report.
|
||||
// We make the assumption that all of the URIs are absolute URIs; this
|
||||
|
@ -323,7 +294,7 @@ ContentSecurityPolicy.prototype = {
|
|||
|
||||
try {
|
||||
var chan = Services.io.newChannel(uris[i], null, null);
|
||||
if(!chan) {
|
||||
if (!chan) {
|
||||
CSPdebug("Error creating channel for " + uris[i]);
|
||||
continue;
|
||||
}
|
||||
|
@ -338,7 +309,7 @@ ContentSecurityPolicy.prototype = {
|
|||
|
||||
// we need to set an nsIChannelEventSink on the channel object
|
||||
// so we can tell it to not follow redirects when posting the reports
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink();
|
||||
chan.notificationCallbacks = new CSPReportRedirectSink(this._policy);
|
||||
|
||||
chan.QueryInterface(Ci.nsIUploadChannel)
|
||||
.setUploadStream(content, "application/json", content.available());
|
||||
|
@ -369,8 +340,8 @@ ContentSecurityPolicy.prototype = {
|
|||
} catch(e) {
|
||||
// it's possible that the URI was invalid, just log a
|
||||
// warning and skip over that.
|
||||
CSPWarning(CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]), this.innerWindowID);
|
||||
CSPWarning(CSPLocalizer.getFormatStr("errorWas", [e.toString()]), this.innerWindowID);
|
||||
this._policy.warn(CSPLocalizer.getFormatStr("triedToSendReport", [uris[i]]));
|
||||
this._policy.warn(CSPLocalizer.getFormatStr("errorWas", [e.toString()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -552,7 +523,8 @@ ContentSecurityPolicy.prototype = {
|
|||
// The POST of the violation report (if it happens) should not follow
|
||||
// redirects, per the spec. hence, we implement an nsIChannelEventSink
|
||||
// with an object so we can tell XHR to abort if a redirect happens.
|
||||
function CSPReportRedirectSink() {
|
||||
function CSPReportRedirectSink(policy) {
|
||||
this._policy = policy;
|
||||
}
|
||||
|
||||
CSPReportRedirectSink.prototype = {
|
||||
|
@ -575,7 +547,7 @@ CSPReportRedirectSink.prototype = {
|
|||
// nsIChannelEventSink
|
||||
asyncOnChannelRedirect: function channel_redirect(oldChannel, newChannel,
|
||||
flags, callback) {
|
||||
CSPWarning(CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec]));
|
||||
this._policy.warn(CSPLocalizer.getFormatStr("reportPostRedirect", [oldChannel.URI.asciiSpec]));
|
||||
|
||||
// cancel the old channel so XHR failure callback happens
|
||||
oldChannel.cancel(Cr.NS_ERROR_ABORT);
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsCCUncollectableMarker.h"
|
||||
#include "nsGkAtoms.h"
|
||||
|
||||
#include "mozilla/dom/HTMLCollectionBinding.h"
|
||||
#include "mozilla/dom/NodeListBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
// Form related includes
|
||||
#include "nsIDOMHTMLFormElement.h"
|
||||
|
@ -163,7 +163,13 @@ JSObject*
|
|||
nsSimpleContentList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
|
||||
JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::NodeList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
// nsFormContentList
|
||||
|
@ -297,7 +303,13 @@ JSObject*
|
|||
nsCacheableFuncStringNodeList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return oldproxybindings::NodeList::create(cx, scope, this, triedToWrap);
|
||||
JSObject* obj = NodeListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::NodeList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
|
||||
|
@ -305,7 +317,13 @@ JSObject*
|
|||
nsCacheableFuncStringHTMLCollection::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return oldproxybindings::HTMLCollection::create(cx, scope, this, triedToWrap);
|
||||
JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::HTMLCollection::create(cx, scope, this);
|
||||
}
|
||||
|
||||
// Hashtable for storing nsCacheableFuncStringContentList
|
||||
|
@ -534,8 +552,13 @@ nsContentList::~nsContentList()
|
|||
JSObject*
|
||||
nsContentList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::HTMLCollection::create(cx, scope, this);
|
||||
}
|
||||
|
||||
DOMCI_DATA(ContentList, nsContentList)
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
|
||||
#include "plbase64.h"
|
||||
#include "prmem.h"
|
||||
#include "mozilla/dom/FileListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -680,7 +681,13 @@ JSObject*
|
|||
nsDOMFileList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::FileList::create(cx, scope, this, triedToWrap);
|
||||
JSObject* obj = FileListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::FileList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
nsIDOMFile*
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include "nsDOMSettableTokenList.h"
|
||||
#include "mozilla/dom/DOMSettableTokenListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
|
||||
|
@ -51,6 +52,14 @@ JSObject*
|
|||
nsDOMSettableTokenList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::DOMSettableTokenList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::DOMSettableTokenListBinding::Wrap(cx, scope,
|
||||
this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::DOMSettableTokenList::create(cx, scope,
|
||||
this);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsError.h"
|
||||
#include "nsGenericElement.h"
|
||||
#include "mozilla/dom/DOMTokenListBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
|
@ -312,7 +313,12 @@ nsDOMTokenList::ToString(nsAString& aResult)
|
|||
JSObject*
|
||||
nsDOMTokenList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::DOMTokenList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = DOMTokenListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::DOMTokenList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
|
|
|
@ -2458,7 +2458,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
|
|||
// ----- Figure out if we need to apply an app default CSP
|
||||
bool applyAppDefaultCSP = false;
|
||||
nsIPrincipal* principal = NodePrincipal();
|
||||
PRUint16 appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
bool unknownAppId;
|
||||
if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
|
||||
!unknownAppId &&
|
||||
|
@ -8424,7 +8424,7 @@ nsDocument::NotifyAudioAvailableListener()
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::RemoveImage(imgIRequest* aImage)
|
||||
nsDocument::RemoveImage(imgIRequest* aImage, uint32_t aFlags)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aImage);
|
||||
|
||||
|
@ -8460,10 +8460,12 @@ nsDocument::RemoveImage(imgIRequest* aImage)
|
|||
rv = NS_SUCCEEDED(rv) ? rv2 : rv;
|
||||
}
|
||||
|
||||
// Request that the image be discarded if nobody else holds a lock on it.
|
||||
// Do this even if !mLockingImages, because even if we didn't just unlock
|
||||
// this image, it might still be a candidate for discarding.
|
||||
aImage->RequestDiscard();
|
||||
if (aFlags & REQUEST_DISCARD) {
|
||||
// Request that the image be discarded if nobody else holds a lock on it.
|
||||
// Do this even if !mLockingImages, because even if we didn't just unlock
|
||||
// this image, it might still be a candidate for discarding.
|
||||
aImage->RequestDiscard();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
|
|
@ -915,7 +915,7 @@ public:
|
|||
virtual Element *LookupImageElement(const nsAString& aElementId);
|
||||
|
||||
virtual NS_HIDDEN_(nsresult) AddImage(imgIRequest* aImage);
|
||||
virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
|
||||
virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage, uint32_t aFlags);
|
||||
virtual NS_HIDDEN_(nsresult) SetImageLockingState(bool aLocked);
|
||||
|
||||
// AddPlugin adds a plugin-related element to mPlugins when the element is
|
||||
|
|
|
@ -102,7 +102,6 @@
|
|||
#include "nsRuleProcessorData.h"
|
||||
#include "nsAsyncDOMEvent.h"
|
||||
#include "nsTextNode.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsIXULDocument.h"
|
||||
|
|
|
@ -1183,9 +1183,9 @@ nsImageLoadingContent::UnbindFromTree(bool aDeep, bool aNullParent)
|
|||
pusher.PushNull();
|
||||
|
||||
if (mCurrentRequestFlags & REQUEST_SHOULD_BE_TRACKED)
|
||||
doc->RemoveImage(mCurrentRequest);
|
||||
doc->RemoveImage(mCurrentRequest, nsIDocument::REQUEST_DISCARD);
|
||||
if (mPendingRequestFlags & REQUEST_SHOULD_BE_TRACKED)
|
||||
doc->RemoveImage(mPendingRequest);
|
||||
doc->RemoveImage(mPendingRequest, nsIDocument::REQUEST_DISCARD);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1227,7 +1227,7 @@ nsImageLoadingContent::UntrackImage(imgIRequest* aImage)
|
|||
// all locked images on destruction.
|
||||
nsIDocument* doc = GetOurCurrentDoc();
|
||||
if (doc)
|
||||
return doc->RemoveImage(aImage);
|
||||
return doc->RemoveImage(aImage, nsIDocument::REQUEST_DISCARD);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -228,15 +228,7 @@ nsInProcessTabChildGlobal::DelayedDisconnect()
|
|||
mOwner = nullptr;
|
||||
|
||||
// Fire the "unload" event
|
||||
nsCOMPtr<nsIDOMEvent> event;
|
||||
NS_NewDOMEvent(getter_AddRefs(event), nullptr, nullptr);
|
||||
if (event) {
|
||||
event->InitEvent(NS_LITERAL_STRING("unload"), false, false);
|
||||
event->SetTrusted(true);
|
||||
|
||||
bool dummy;
|
||||
nsDOMEventTargetHelper::DispatchEvent(event, &dummy);
|
||||
}
|
||||
nsDOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
|
||||
|
||||
// Continue with the Disconnect cleanup
|
||||
nsCOMPtr<nsIDOMWindow> win = do_GetInterface(mDocShell);
|
||||
|
|
|
@ -76,14 +76,14 @@ nsMixedContentBlocker::~nsMixedContentBlocker()
|
|||
NS_IMPL_ISUPPORTS1(nsMixedContentBlocker, nsIContentPolicy)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMixedContentBlocker::ShouldLoad(PRUint32 aContentType,
|
||||
nsMixedContentBlocker::ShouldLoad(uint32_t aContentType,
|
||||
nsIURI* aContentLocation,
|
||||
nsIURI* aRequestingLocation,
|
||||
nsISupports* aRequestingContext,
|
||||
const nsACString& aMimeGuess,
|
||||
nsISupports* aExtra,
|
||||
nsIPrincipal* aRequestPrincipal,
|
||||
PRInt16* aDecision)
|
||||
int16_t* aDecision)
|
||||
{
|
||||
// Default policy: allow the load if we find no reason to block it.
|
||||
*aDecision = nsIContentPolicy::ACCEPT;
|
||||
|
@ -186,14 +186,14 @@ nsMixedContentBlocker::ShouldLoad(PRUint32 aContentType,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMixedContentBlocker::ShouldProcess(PRUint32 aContentType,
|
||||
nsMixedContentBlocker::ShouldProcess(uint32_t aContentType,
|
||||
nsIURI* aContentLocation,
|
||||
nsIURI* aRequestingLocation,
|
||||
nsISupports* aRequestingContext,
|
||||
const nsACString& aMimeGuess,
|
||||
nsISupports* aExtra,
|
||||
nsIPrincipal* aRequestPrincipal,
|
||||
PRInt16* aDecision)
|
||||
int16_t* aDecision)
|
||||
{
|
||||
if(!aContentLocation) {
|
||||
// aContentLocation may be null when a plugin is loading without an associated URI resource
|
||||
|
|
|
@ -159,29 +159,29 @@ test(
|
|||
function test_CSPSource_fromString() {
|
||||
// can't do these tests because "self" is not defined.
|
||||
//"basic source should not be null.");
|
||||
do_check_neq(null, CSPSource.fromString("a.com", "http://abc.com"));
|
||||
do_check_neq(null, CSPSource.fromString("a.com", undefined, "http://abc.com"));
|
||||
|
||||
//"ldh characters should all work for host.");
|
||||
do_check_neq(null, CSPSource.fromString("a2-c.com", "https://a.com"));
|
||||
do_check_neq(null, CSPSource.fromString("a2-c.com", undefined, "https://a.com"));
|
||||
|
||||
//"wildcard should work in first token for host.");
|
||||
do_check_neq(null, CSPSource.fromString("*.a.com", "http://abc.com"));
|
||||
do_check_neq(null, CSPSource.fromString("*.a.com", undefined, "http://abc.com"));
|
||||
|
||||
//print(" --- Ignore the following two errors if they print ---");
|
||||
//"wildcard should not work in non-first token for host.");
|
||||
do_check_eq(null, CSPSource.fromString("x.*.a.com", "http://a.com"));
|
||||
do_check_eq(null, CSPSource.fromString("x.*.a.com", undefined, "http://a.com"));
|
||||
|
||||
//"funny characters (#) should not work for host.");
|
||||
do_check_eq(null, CSPSource.fromString("a#2-c.com", "http://a.com"));
|
||||
do_check_eq(null, CSPSource.fromString("a#2-c.com", undefined, "http://a.com"));
|
||||
|
||||
//print(" --- Stop ignoring errors that print ---\n");
|
||||
|
||||
//"failed to parse host with port.");
|
||||
do_check_neq(null, CSPSource.create("a.com:23", "http://a.com"));
|
||||
do_check_neq(null, CSPSource.create("a.com:23", undefined, "http://a.com"));
|
||||
//"failed to parse host with scheme.");
|
||||
do_check_neq(null, CSPSource.create("https://a.com", "http://a.com"));
|
||||
do_check_neq(null, CSPSource.create("https://a.com", undefined, "http://a.com"));
|
||||
//"failed to parse host with scheme and port.");
|
||||
do_check_neq(null, CSPSource.create("https://a.com:200", "http://a.com"));
|
||||
do_check_neq(null, CSPSource.create("https://a.com:200", undefined, "http://a.com"));
|
||||
|
||||
//Check to make sure we don't match multiple instances with regex
|
||||
do_check_eq(null, CSPSource.create("http://foo.com:bar.com:23"));
|
||||
|
@ -193,7 +193,7 @@ test(
|
|||
test(
|
||||
function test_CSPSource_fromString_withSelf() {
|
||||
var src;
|
||||
src = CSPSource.create("a.com", "https://foobar.com:443");
|
||||
src = CSPSource.create("a.com", undefined, "https://foobar.com:443");
|
||||
//"src should inherit port *
|
||||
do_check_true(src.permits("https://a.com:443"));
|
||||
//"src should inherit and require https scheme
|
||||
|
@ -201,7 +201,7 @@ test(
|
|||
//"src should inherit scheme 'https'"
|
||||
do_check_true(src.permits("https://a.com"));
|
||||
|
||||
src = CSPSource.create("http://a.com", "https://foobar.com:443");
|
||||
src = CSPSource.create("http://a.com", undefined, "https://foobar.com:443");
|
||||
//"src should inherit and require http scheme"
|
||||
do_check_false(src.permits("https://a.com"));
|
||||
//"src should inherit scheme 'http'"
|
||||
|
@ -210,7 +210,7 @@ test(
|
|||
//"src should inherit default port for 'http'"
|
||||
do_check_true(src.permits("http://a.com:80"));
|
||||
|
||||
src = CSPSource.create("'self'", "https://foobar.com:443");
|
||||
src = CSPSource.create("'self'", undefined, "https://foobar.com:443");
|
||||
//"src should inherit port *
|
||||
do_check_true(src.permits("https://foobar.com:443"));
|
||||
//"src should inherit and require https scheme
|
||||
|
@ -220,7 +220,7 @@ test(
|
|||
//"src should reject other hosts"
|
||||
do_check_false(src.permits("https://a.com"));
|
||||
|
||||
src = CSPSource.create("javascript:", "https://foobar.com:443");
|
||||
src = CSPSource.create("javascript:", undefined, "https://foobar.com:443");
|
||||
//"hostless schemes should be parseable."
|
||||
var aUri = NetUtil.newURI("javascript:alert('foo');");
|
||||
do_check_true(src.permits(aUri));
|
||||
|
@ -260,7 +260,7 @@ test(
|
|||
function test_CSPSourceList_fromString_twohost() {
|
||||
var str = "foo.bar:21 https://ras.bar";
|
||||
var parsed = "http://foo.bar:21 https://ras.bar:443";
|
||||
var sd = CSPSourceList.fromString(str, URI("http://self.com:80"));
|
||||
var sd = CSPSourceList.fromString(str, undefined, URI("http://self.com:80"));
|
||||
//"two-host list should parse"
|
||||
do_check_neq(null,sd);
|
||||
//"two-host list should parse to two hosts"
|
||||
|
@ -272,8 +272,9 @@ test(
|
|||
test(
|
||||
function test_CSPSourceList_permits() {
|
||||
var nullSourceList = CSPSourceList.fromString("'none'");
|
||||
var simpleSourceList = CSPSourceList.fromString("a.com", URI("http://self.com"));
|
||||
var simpleSourceList = CSPSourceList.fromString("a.com", undefined, URI("http://self.com"));
|
||||
var doubleSourceList = CSPSourceList.fromString("https://foo.com http://bar.com:88",
|
||||
undefined,
|
||||
URI("http://self.com:88"));
|
||||
var allSourceList = CSPSourceList.fromString("*");
|
||||
var allAndMoreSourceList = CSPSourceList.fromString("* https://bar.com 'none'");
|
||||
|
@ -680,7 +681,7 @@ test(
|
|||
*/
|
||||
|
||||
var src;
|
||||
src = CSPSource.create("a.com", "https://foobar.com:4443");
|
||||
src = CSPSource.create("a.com", undefined, "https://foobar.com:4443");
|
||||
//"src should inherit and require https scheme
|
||||
do_check_false(src.permits("http://a.com"));
|
||||
//"src should inherit scheme 'https'"
|
||||
|
@ -688,7 +689,7 @@ test(
|
|||
//"src should get default port
|
||||
do_check_true(src.permits("https://a.com:443"));
|
||||
|
||||
src = CSPSource.create("http://a.com", "https://foobar.com:4443");
|
||||
src = CSPSource.create("http://a.com", undefined, "https://foobar.com:4443");
|
||||
//"src should require http scheme"
|
||||
do_check_false(src.permits("https://a.com"));
|
||||
//"src should keep scheme 'http'"
|
||||
|
@ -696,7 +697,7 @@ test(
|
|||
//"src should inherit default port for 'http'"
|
||||
do_check_true(src.permits("http://a.com:80"));
|
||||
|
||||
src = CSPSource.create("'self'", "https://foobar.com:4443");
|
||||
src = CSPSource.create("'self'", undefined, "https://foobar.com:4443");
|
||||
//"src should inherit nonstandard port from self
|
||||
do_check_true(src.permits("https://foobar.com:4443"));
|
||||
do_check_false(src.permits("https://foobar.com"));
|
||||
|
@ -716,9 +717,9 @@ test(
|
|||
* doesn't happen.
|
||||
*/
|
||||
|
||||
var p_none = CSPSourceList.fromString("'none'", "http://foo.com", false);
|
||||
var p_all = CSPSourceList.fromString("*", "http://foo.com", false);
|
||||
var p_one = CSPSourceList.fromString("bar.com", "http://foo.com", false);
|
||||
var p_none = CSPSourceList.fromString("'none'", undefined, "http://foo.com", false);
|
||||
var p_all = CSPSourceList.fromString("*", undefined, "http://foo.com", false);
|
||||
var p_one = CSPSourceList.fromString("bar.com", undefined, "http://foo.com", false);
|
||||
|
||||
do_check_false(p_none.equals(p_all));
|
||||
do_check_false(p_none.equals(p_one));
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "nsDOMJSUtils.h"
|
||||
#include "prprf.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsDOMEvent.h"
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMEventTargetHelper)
|
||||
|
||||
|
@ -204,6 +205,26 @@ nsDOMEventTargetHelper::DispatchEvent(nsIDOMEvent* aEvent, bool* aRetVal)
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMEventTargetHelper::DispatchTrustedEvent(const nsAString& aEventName)
|
||||
{
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(aEventName, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent* event)
|
||||
{
|
||||
nsresult rv = event->SetTrusted(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool dummy;
|
||||
return DispatchEvent(event, &dummy);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
|
||||
JSContext* aCx,
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
|
||||
class nsDOMEvent;
|
||||
|
||||
class nsDOMEventTargetHelper : public nsIDOMEventTarget,
|
||||
public nsWrapperCache
|
||||
{
|
||||
|
@ -114,6 +116,10 @@ public:
|
|||
bool HasOrHasHadOwner() { return mHasOrHasHadOwner; }
|
||||
protected:
|
||||
nsRefPtr<nsEventListenerManager> mListenerManager;
|
||||
// Dispatch a trusted, non-cancellable and non-bubbling event to |this|.
|
||||
nsresult DispatchTrustedEvent(const nsAString& aEventName);
|
||||
// Make |event| trusted and dispatch |aEvent| to |this|.
|
||||
nsresult DispatchTrustedEvent(nsIDOMEvent* aEvent);
|
||||
private:
|
||||
// These may be null (native callers or xpcshell).
|
||||
nsPIDOMWindow* mOwner; // Inner window.
|
||||
|
|
|
@ -173,13 +173,8 @@ nsEventListenerManager::GetInnerWindowForTarget()
|
|||
return node->OwnerDoc()->GetInnerWindow();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
|
||||
if (window) {
|
||||
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
|
||||
return window;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
return window;
|
||||
}
|
||||
|
||||
already_AddRefed<nsPIDOMWindow>
|
||||
|
@ -290,7 +285,10 @@ nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
|
|||
} else if (aTypeAtom == nsGkAtoms::ondevicemotion) {
|
||||
EnableDevice(NS_DEVICE_MOTION);
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoztimechange) {
|
||||
EnableTimeChangeNotifications();
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->EnableTimeChangeNotifications();
|
||||
}
|
||||
} else if (aTypeAtom == nsGkAtoms::onmoznetworkupload) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
|
@ -348,13 +346,11 @@ nsEventListenerManager::IsDeviceType(uint32_t aType)
|
|||
void
|
||||
nsEventListenerManager::EnableDevice(uint32_t aType)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
|
||||
|
||||
switch (aType) {
|
||||
case NS_DEVICE_ORIENTATION:
|
||||
window->EnableDeviceSensor(SENSOR_ORIENTATION);
|
||||
|
@ -380,13 +376,11 @@ nsEventListenerManager::EnableDevice(uint32_t aType)
|
|||
void
|
||||
nsEventListenerManager::DisableDevice(uint32_t aType)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
|
||||
|
||||
switch (aType) {
|
||||
case NS_DEVICE_ORIENTATION:
|
||||
window->DisableDeviceSensor(SENSOR_ORIENTATION);
|
||||
|
@ -452,7 +446,10 @@ nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
|
|||
if (deviceType && typeCount == 0) {
|
||||
DisableDevice(aType);
|
||||
} else if (timeChangeEvent && typeCount == 0) {
|
||||
DisableTimeChangeNotifications();
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
window->DisableTimeChangeNotifications();
|
||||
}
|
||||
} else if (networkEvent && typeCount == 0) {
|
||||
nsCOMPtr<nsPIDOMWindow> window = GetTargetAsInnerWindow();
|
||||
if (window) {
|
||||
|
@ -624,11 +621,8 @@ nsEventListenerManager::SetEventHandler(nsIAtom *aName,
|
|||
doc = node->OwnerDoc();
|
||||
global = doc->GetScriptGlobalObject();
|
||||
} else {
|
||||
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mTarget));
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetTargetAsInnerWindow();
|
||||
if (win) {
|
||||
NS_ASSERTION(win->IsInnerWindow(),
|
||||
"Event listener added to outer window!");
|
||||
|
||||
nsCOMPtr<nsIDOMDocument> domdoc;
|
||||
win->GetDocument(getter_AddRefs(domdoc));
|
||||
doc = do_QueryInterface(domdoc);
|
||||
|
@ -1199,27 +1193,3 @@ nsEventListenerManager::UnmarkGrayJSListeners()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsEventListenerManager::EnableTimeChangeNotifications()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
|
||||
window->EnableTimeChangeNotifications();
|
||||
}
|
||||
|
||||
void
|
||||
nsEventListenerManager::DisableTimeChangeNotifications()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
|
||||
if (!window) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
|
||||
window->DisableTimeChangeNotifications();
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ typedef enum
|
|||
struct nsListenerStruct
|
||||
{
|
||||
nsRefPtr<nsIDOMEventListener> mListener;
|
||||
uint32_t mEventType;
|
||||
nsCOMPtr<nsIAtom> mTypeAtom;
|
||||
uint32_t mEventType;
|
||||
uint16_t mFlags;
|
||||
uint8_t mListenerType;
|
||||
bool mListenerIsHandler : 1;
|
||||
|
@ -269,9 +269,6 @@ protected:
|
|||
void EnableDevice(uint32_t aType);
|
||||
void DisableDevice(uint32_t aType);
|
||||
|
||||
void EnableTimeChangeNotifications();
|
||||
void DisableTimeChangeNotifications();
|
||||
|
||||
public:
|
||||
/**
|
||||
* Set the "inline" event listener for aEventName to |v|. This
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "nsClientRect.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/PaintRequestListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
DOMCI_DATA(PaintRequest, nsPaintRequest)
|
||||
|
||||
|
@ -53,6 +55,21 @@ NS_INTERFACE_MAP_END
|
|||
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPaintRequestList)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPaintRequestList)
|
||||
|
||||
JSObject*
|
||||
nsPaintRequestList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
JSObject* obj = mozilla::dom::PaintRequestListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::PaintRequestList::create(cx, scope,
|
||||
this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPaintRequestList::GetLength(uint32_t* aLength)
|
||||
{
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include "nsIDOMPaintRequestList.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "dombindings.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
class nsPaintRequest MOZ_FINAL : public nsIDOMPaintRequest
|
||||
{
|
||||
|
@ -44,12 +44,7 @@ public:
|
|||
NS_DECL_NSIDOMPAINTREQUESTLIST
|
||||
|
||||
virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::PaintRequestList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
}
|
||||
|
||||
bool *triedToWrap);
|
||||
nsISupports* GetParentObject()
|
||||
{
|
||||
return mParent;
|
||||
|
|
|
@ -51,6 +51,11 @@ function runTest() {
|
|||
[true, false],
|
||||
[true, true],
|
||||
];
|
||||
|
||||
// grab the refresh driver, since we want to make sure
|
||||
// async scrolls happen in deterministic time
|
||||
winUtils.advanceTimeAndRefresh(1000);
|
||||
|
||||
function nextTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
if (!outstandingTests.length) {
|
||||
|
@ -63,7 +68,9 @@ function runTest() {
|
|||
let [ctrlKey, isMomentum] = outstandingTests.shift();
|
||||
let scrollTopBefore = scrollbox.scrollTop;
|
||||
let zoomFactorBefore = winUtils.fullZoom;
|
||||
|
||||
sendTouchpadScrollMotion(scrollbox, 1, ctrlKey, isMomentum);
|
||||
winUtils.advanceTimeAndRefresh(1000); // force scrolling to happen
|
||||
|
||||
setTimeout(function () {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
@ -83,6 +90,8 @@ function runTest() {
|
|||
}
|
||||
// Revert the effect.
|
||||
sendTouchpadScrollMotion(scrollbox, -1, ctrlKey, isMomentum);
|
||||
winUtils.advanceTimeAndRefresh(1000); // force scrolling to happen
|
||||
|
||||
setTimeout(nextTest, 20);
|
||||
}, 20);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
#include "nsVariant.h"
|
||||
#include "nsDOMSettableTokenList.h"
|
||||
#include "nsAttrValue.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
|
||||
|
||||
DOMCI_DATA(HTMLPropertiesCollection, mozilla::dom::HTMLPropertiesCollection)
|
||||
DOMCI_DATA(PropertyNodeList, mozilla::dom::PropertyNodeList)
|
||||
|
@ -110,8 +110,14 @@ JSObject*
|
|||
HTMLPropertiesCollection::WrapObject(JSContext* cx, JSObject* scope,
|
||||
bool* triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::HTMLPropertiesCollection::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = HTMLPropertiesCollectionBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::HTMLPropertiesCollection::create(cx, scope, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -446,8 +452,13 @@ JSObject*
|
|||
PropertyNodeList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::PropertyNodeList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = PropertyNodeListBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::PropertyNodeList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(PropertyNodeList)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "nsDOMClassInfoID.h"
|
||||
|
||||
#include "nsPresContext.h"
|
||||
#include "mozilla/dom/ClientRectListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
DOMCI_DATA(ClientRect, nsClientRect)
|
||||
|
@ -106,8 +107,14 @@ nsClientRectList::GetItemAt(uint32_t aIndex)
|
|||
JSObject*
|
||||
nsClientRectList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::ClientRectList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::ClientRectListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::ClientRectList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
static double
|
||||
|
|
|
@ -51,9 +51,9 @@
|
|||
#include "nsIConstraintValidation.h"
|
||||
|
||||
#include "nsIDOMHTMLButtonElement.h"
|
||||
#include "mozilla/dom/HTMLCollectionBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "nsSandboxFlags.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -125,8 +125,13 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = HTMLCollectionBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::HTMLCollection::create(cx, scope, this);
|
||||
}
|
||||
|
||||
nsHTMLFormElement* mForm; // WEAK - the form owns me
|
||||
|
|
|
@ -35,8 +35,8 @@
|
|||
#include "nsEventDispatcher.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozAutoDocUpdate.h"
|
||||
#include "mozilla/dom/HTMLOptionsCollectionBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -2009,8 +2009,13 @@ JSObject*
|
|||
nsHTMLOptionCollection::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::HTMLOptionsCollection::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = HTMLOptionsCollectionBinding::Wrap(cx, scope, this, triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return oldproxybindings::HTMLOptionsCollection::create(cx, scope, this);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -25,9 +25,8 @@
|
|||
#include "nsIDOMElement.h"
|
||||
#include "nsIHTMLCollection.h"
|
||||
#include "nsHTMLStyleSheet.h"
|
||||
#include "mozilla/dom/HTMLCollectionBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -62,8 +61,15 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::HTMLCollectionBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::HTMLCollection::create(cx, scope,
|
||||
this);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "SVGAnimatedLengthList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/SVGLengthListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
// See the comment in this file's header.
|
||||
|
@ -76,8 +77,14 @@ NS_INTERFACE_MAP_END
|
|||
JSObject*
|
||||
DOMSVGLengthList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::SVGLengthList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::SVGLengthListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::SVGLengthList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
nsIDOMSVGLength*
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "SVGAnimatedNumberList.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/SVGNumberListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
// See the comment in this file's header.
|
||||
|
@ -77,8 +78,14 @@ NS_INTERFACE_MAP_END
|
|||
JSObject*
|
||||
DOMSVGNumberList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::SVGNumberList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::SVGNumberListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::SVGNumberList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
nsIDOMSVGNumber*
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#include "SVGPathSegUtils.h"
|
||||
#include "mozilla/dom/SVGPathSegListBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
|
@ -83,8 +84,15 @@ DOMSVGPathSegList::~DOMSVGPathSegList()
|
|||
JSObject*
|
||||
DOMSVGPathSegList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::SVGPathSegList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::SVGPathSegListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::SVGPathSegList::create(cx, scope,
|
||||
this);
|
||||
}
|
||||
|
||||
nsIDOMSVGPathSeg*
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "nsCOMPtr.h"
|
||||
#include "nsSVGAttrTearoffTable.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/SVGPointListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
// See the comment in this file's header.
|
||||
|
@ -102,8 +103,14 @@ DOMSVGPointList::~DOMSVGPointList()
|
|||
JSObject*
|
||||
DOMSVGPointList::WrapObject(JSContext *cx, JSObject *scope, bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::SVGPointList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::SVGPointListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::SVGPointList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
nsIDOMSVGPoint*
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "SVGAnimatedTransformList.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/SVGTransformListBinding.h"
|
||||
#include "dombindings.h"
|
||||
#include "nsError.h"
|
||||
|
||||
|
@ -77,8 +78,15 @@ JSObject*
|
|||
DOMSVGTransformList::WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::SVGTransformList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::SVGTransformListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::SVGTransformList::create(cx, scope,
|
||||
this);
|
||||
}
|
||||
|
||||
nsIDOMSVGTransform*
|
||||
|
|
|
@ -25,12 +25,12 @@ var text = document.getElementById("text"),
|
|||
function CheckList(aListObject, aExpectedListLength, aListDescription)
|
||||
{
|
||||
is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".numberOfItems");
|
||||
is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".length");
|
||||
is(aListObject.length, aExpectedListLength, aListDescription + ".length");
|
||||
for (let i = 0; i < aListObject.length; i++) {
|
||||
let item = aListObject.getItem(i);
|
||||
ok(aListObject[i] === item, aListDescription + "[" + i + "]");
|
||||
}
|
||||
ok(aListObject[aListObject.length] === void 0, aListDescription + "[outOfBounds]");
|
||||
is(typeof(aListObject[aListObject.length]), "undefined", aListDescription + "[outOfBounds]");
|
||||
}
|
||||
|
||||
var tests = [
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "nsBindingManager.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/NodeListBinding.h"
|
||||
#include "dombindings.h"
|
||||
|
||||
// ==================================================================
|
||||
|
@ -79,8 +80,14 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext *cx, JSObject *scope,
|
||||
bool *triedToWrap)
|
||||
{
|
||||
return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this,
|
||||
triedToWrap);
|
||||
JSObject* obj = mozilla::dom::NodeListBinding::Wrap(cx, scope, this,
|
||||
triedToWrap);
|
||||
if (obj || *triedToWrap) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
*triedToWrap = true;
|
||||
return mozilla::dom::oldproxybindings::NodeList::create(cx, scope, this);
|
||||
}
|
||||
|
||||
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ANONYMOUS_CONTENT_LIST_IID)
|
||||
|
|
|
@ -1,39 +1,7 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
|
|
|
@ -1,38 +1,6 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
|
|
|
@ -206,9 +206,13 @@
|
|||
#include "xp_mcom.h"
|
||||
#define O_ACCMODE 3 /* Mask for file access modes */
|
||||
#define EFTYPE 2000
|
||||
PR_BEGIN_EXTERN_C
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int mkstemp(const char *path);
|
||||
PR_END_EXTERN_C
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* MACINTOSH */
|
||||
|
||||
#if !defined(_WINDOWS) && !defined(macintosh)
|
||||
|
@ -391,7 +395,9 @@ typedef struct {
|
|||
}
|
||||
#endif
|
||||
|
||||
PR_BEGIN_EXTERN_C
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern DB *
|
||||
dbopen (const char *, int, int, DBTYPE, const void *);
|
||||
|
@ -408,6 +414,8 @@ DB *__rec_open (const char *, int, int, const RECNOINFO *, int);
|
|||
void __dbpanic (DB *dbp);
|
||||
#endif
|
||||
|
||||
PR_END_EXTERN_C
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_DB_H_ */
|
||||
|
|
|
@ -1,39 +1,7 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
|
|
|
@ -1,38 +1,6 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
#//------------------------------------------------------------------------
|
||||
|
|
|
@ -1,39 +1,7 @@
|
|||
#
|
||||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is mozilla.org code.
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Netscape Communications Corporation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 1998
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DEPTH = ../..
|
||||
topsrcdir = @top_srcdir@
|
||||
|
|
|
@ -1,39 +1,7 @@
|
|||
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* use sequental numbers printed to strings
|
||||
* to store lots and lots of entries in the
|
||||
|
|
|
@ -193,6 +193,10 @@
|
|||
#include "mozilla/Telemetry.h"
|
||||
#include "nsISecurityUITelemetry.h"
|
||||
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
#include "nsIPrivateBrowsingService.h"
|
||||
#endif
|
||||
|
||||
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
|
||||
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
@ -2722,12 +2726,23 @@ nsDocShell::SetDocLoaderParent(nsDocLoader * aParent)
|
|||
}
|
||||
SetAllowDNSPrefetch(value);
|
||||
}
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
// Set the PB flag on the docshell based on the global PB mode for now
|
||||
nsCOMPtr<nsIPrivateBrowsingService> pbs =
|
||||
do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
|
||||
if (pbs) {
|
||||
bool inPrivateBrowsing = false;
|
||||
pbs->GetPrivateBrowsingEnabled(&inPrivateBrowsing);
|
||||
SetUsePrivateBrowsing(inPrivateBrowsing);
|
||||
}
|
||||
#else
|
||||
nsCOMPtr<nsILoadContext> parentAsLoadContext(do_QueryInterface(parent));
|
||||
if (parentAsLoadContext &&
|
||||
NS_SUCCEEDED(parentAsLoadContext->GetUsePrivateBrowsing(&value)))
|
||||
{
|
||||
SetUsePrivateBrowsing(value);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(parent));
|
||||
if (parentURIListener)
|
||||
|
|
|
@ -187,8 +187,6 @@ WebappsRegistry.prototype = {
|
|||
|
||||
uninit: function() {
|
||||
this._mgmt = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
["Webapps:Install:Return:OK"]);
|
||||
},
|
||||
|
||||
// mozIDOMApplicationRegistry2 implementation
|
||||
|
@ -484,9 +482,6 @@ WebappsApplication.prototype = {
|
|||
|
||||
uninit: function() {
|
||||
this._onprogress = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache",
|
||||
"Webapps:PackageEvent"]);
|
||||
},
|
||||
|
||||
_fireEvent: function(aName, aHandler) {
|
||||
|
@ -649,8 +644,6 @@ WebappsApplicationMgmt.prototype = {
|
|||
uninit: function() {
|
||||
this._oninstall = null;
|
||||
this._onuninstall = null;
|
||||
cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
|
||||
["Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]);
|
||||
},
|
||||
|
||||
applyDownload: function(aApp) {
|
||||
|
|
|
@ -159,9 +159,9 @@ let DOMApplicationRegistry = {
|
|||
"Webapps:Launch", "Webapps:GetAll",
|
||||
"Webapps:InstallPackage", "Webapps:GetBasePath",
|
||||
"Webapps:GetList", "Webapps:RegisterForMessages",
|
||||
"Webapps:UnregisterForMessages",
|
||||
"Webapps:CancelDownload", "Webapps:CheckForUpdate",
|
||||
"Webapps::Download", "Webapps::ApplyDownload"];
|
||||
"Webapps::Download", "Webapps::ApplyDownload",
|
||||
"child-process-shutdown"];
|
||||
|
||||
this.frameMessages = ["Webapps:ClearBrowserData"];
|
||||
|
||||
|
@ -512,17 +512,17 @@ let DOMApplicationRegistry = {
|
|||
}, this);
|
||||
},
|
||||
|
||||
removeMessageListener: function(aMsgNames, aMm) {
|
||||
aMsgNames.forEach(function (aMsgName) {
|
||||
if (!(aMsgName in this.children)) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeMessageListener: function(aMm) {
|
||||
for (let i = this.children.length - 1; i >= 0; i -= 1) {
|
||||
msg = this.children[i];
|
||||
|
||||
let index;
|
||||
if ((index = this.children[aMsgName].indexOf(aMm)) != -1) {
|
||||
this.children[aMsgName].splice(index, 1);
|
||||
if ((index = msg.indexOf(aMm)) != -1) {
|
||||
debug("Remove dead mm at index " + index);
|
||||
msg.splice(index, 1);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
|
@ -542,7 +542,7 @@ let DOMApplicationRegistry = {
|
|||
}
|
||||
}
|
||||
|
||||
let msg = aMessage.json;
|
||||
let msg = aMessage.data || {};
|
||||
let mm = aMessage.target;
|
||||
msg.mm = mm;
|
||||
|
||||
|
@ -585,9 +585,6 @@ let DOMApplicationRegistry = {
|
|||
case "Webapps:RegisterForMessages":
|
||||
this.addMessageListener(msg, mm);
|
||||
break;
|
||||
case "Webapps:UnregisterForMessages":
|
||||
this.removeMessageListener(msg, mm);
|
||||
break;
|
||||
case "Webapps:GetList":
|
||||
this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], mm);
|
||||
return this.webapps;
|
||||
|
@ -610,6 +607,9 @@ let DOMApplicationRegistry = {
|
|||
this.onInitDone();
|
||||
}
|
||||
break;
|
||||
case "child-process-shutdown":
|
||||
this.removeMessageListener(mm);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -554,6 +554,7 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
|
|||
#include "WebGLContext.h"
|
||||
#include "nsICanvasRenderingContextInternal.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/HTMLCollectionBinding.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
|
|
@ -283,23 +283,7 @@ nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration)
|
|||
"Invalid orientation value passed to notify method!");
|
||||
|
||||
if (mOrientation != previousOrientation) {
|
||||
// TODO: use an helper method, see bug 720768.
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(NS_LITERAL_STRING("mozorientationchange"), false, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = event->SetTrusted(true);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool dummy;
|
||||
rv = DispatchEvent(event, &dummy);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
}
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("mozorientationchange"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -118,23 +118,6 @@ BatteryManager::GetChargingTime(double* aChargingTime)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
BatteryManager::DispatchTrustedEventToSelf(const nsAString& aEventName)
|
||||
{
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(aEventName, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = event->SetTrusted(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool dummy;
|
||||
rv = DispatchEvent(event, &dummy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
BatteryManager::UpdateFromBatteryInfo(const hal::BatteryInformation& aBatteryInfo)
|
||||
{
|
||||
|
@ -161,11 +144,11 @@ BatteryManager::Notify(const hal::BatteryInformation& aBatteryInfo)
|
|||
UpdateFromBatteryInfo(aBatteryInfo);
|
||||
|
||||
if (previousCharging != mCharging) {
|
||||
DispatchTrustedEventToSelf(CHARGINGCHANGE_EVENT_NAME);
|
||||
DispatchTrustedEvent(CHARGINGCHANGE_EVENT_NAME);
|
||||
}
|
||||
|
||||
if (previousLevel != mLevel) {
|
||||
DispatchTrustedEventToSelf(LEVELCHANGE_EVENT_NAME);
|
||||
DispatchTrustedEvent(LEVELCHANGE_EVENT_NAME);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -179,16 +162,16 @@ BatteryManager::Notify(const hal::BatteryInformation& aBatteryInfo)
|
|||
*/
|
||||
if (mCharging != previousCharging) {
|
||||
if (previousRemainingTime != kUnknownRemainingTime) {
|
||||
DispatchTrustedEventToSelf(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
DispatchTrustedEvent(previousCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
}
|
||||
if (mRemainingTime != kUnknownRemainingTime) {
|
||||
DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
}
|
||||
} else if (previousRemainingTime != mRemainingTime) {
|
||||
DispatchTrustedEventToSelf(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
DispatchTrustedEvent(mCharging ? CHARGINGTIMECHANGE_EVENT_NAME
|
||||
: DISCHARGINGTIMECHANGE_EVENT_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,11 +53,6 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
/**
|
||||
* Dispatch a trusted non-cancellable and non-bubbling event to itself.
|
||||
*/
|
||||
nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
|
||||
|
||||
/**
|
||||
* Update the battery information stored in the battery manager object using
|
||||
* a battery information object.
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "mozilla/dom/DOMJSClass.h"
|
||||
#include "mozilla/dom/DOMJSProxyHandler.h"
|
||||
#include "mozilla/dom/NonRefcountedDOMObject.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
|
@ -1145,6 +1146,27 @@ XrayEnumerateProperties(JS::AutoIdVector& props,
|
|||
ConstantSpec* constantSpecs,
|
||||
size_t constantCount);
|
||||
|
||||
// Transfer reference in ptr to smartPtr.
|
||||
template<class T>
|
||||
inline void
|
||||
Take(nsRefPtr<T>& smartPtr, T* ptr)
|
||||
{
|
||||
smartPtr = dont_AddRef(ptr);
|
||||
}
|
||||
|
||||
// Transfer ownership of ptr to smartPtr.
|
||||
template<class T>
|
||||
inline void
|
||||
Take(nsAutoPtr<T>& smartPtr, T* ptr)
|
||||
{
|
||||
smartPtr = ptr;
|
||||
}
|
||||
|
||||
inline void
|
||||
MustInheritFromNonRefcountedDOMObject(NonRefcountedDOMObject*)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
|
|
@ -54,6 +54,23 @@
|
|||
# cannot be prefable, and must ensure that they disallow
|
||||
# XPConnect wrapping. Always true for worker descriptors.
|
||||
# Defaults to true.
|
||||
# * nativeOwnership: Describes how the native object is held. 4 possible
|
||||
# types: worker object ("worker"), non-refcounted object
|
||||
# ("owned"), refcounted non-nsISupports object
|
||||
# ("refcounted") or nsISupports ("nsisupports").
|
||||
# Non-refcounted objects need to inherit from
|
||||
# mozilla::dom::NonRefcountedDOMObject and preferably use
|
||||
# MOZ_COUNT_CTOR/MOZ_COUNT_DTOR in their
|
||||
# constructor/destructor so they participate in leak
|
||||
# logging.
|
||||
# This mostly determines how the finalizer releases the
|
||||
# binding's hold on the native object. For a worker object
|
||||
# it'll call Release, for a non-refcounted object it'll
|
||||
# call delete through XPConnect's deferred finalization
|
||||
# mechanism, for a refcounted object it'll call Release
|
||||
# through XPConnect's deferred finalization mechanism.
|
||||
# Always "worker" for worker descriptors. Defaults to
|
||||
# "nsisupports".
|
||||
#
|
||||
# The following fields are either a string, an array (defaults to an empty
|
||||
# array) or a dictionary with three possible keys (all, getterOnly and
|
||||
|
@ -66,9 +83,6 @@
|
|||
|
||||
DOMInterfaces = {
|
||||
|
||||
'AudioBuffer' : {
|
||||
},
|
||||
|
||||
'mozAudioContext': {
|
||||
'nativeType': 'AudioContext',
|
||||
'implicitJSContext': [ 'createBuffer' ],
|
||||
|
@ -82,12 +96,6 @@ DOMInterfaces = {
|
|||
'concrete': False,
|
||||
},
|
||||
|
||||
'AudioBufferSourceNode': {
|
||||
},
|
||||
|
||||
'AudioDestinationNode': {
|
||||
},
|
||||
|
||||
'Blob': [
|
||||
{
|
||||
'headerFile': 'nsIDOMFile.h',
|
||||
|
@ -158,15 +166,11 @@ DOMInterfaces = {
|
|||
}],
|
||||
|
||||
'Event': [
|
||||
{
|
||||
},
|
||||
{
|
||||
'workers': True,
|
||||
}],
|
||||
|
||||
'EventListener': [
|
||||
{
|
||||
},
|
||||
{
|
||||
'workers': True,
|
||||
}],
|
||||
|
@ -180,7 +184,6 @@ DOMInterfaces = {
|
|||
},
|
||||
{
|
||||
'workers': True,
|
||||
'headerFile': 'mozilla/dom/workers/bindings/EventTarget.h',
|
||||
'concrete': False
|
||||
}],
|
||||
|
||||
|
@ -192,15 +195,11 @@ DOMInterfaces = {
|
|||
'resultNotAddRefed': [ 'item' ]
|
||||
}],
|
||||
|
||||
'FileReaderSync': [
|
||||
{
|
||||
'FileReaderSync': {
|
||||
'workers': True,
|
||||
'headerFile': 'mozilla/dom/workers/bindings/FileReaderSync.h'
|
||||
}],
|
||||
},
|
||||
|
||||
'FormData': [
|
||||
{
|
||||
},
|
||||
{
|
||||
'workers': True,
|
||||
}],
|
||||
|
@ -365,7 +364,6 @@ DOMInterfaces = {
|
|||
},
|
||||
{
|
||||
'workers': True,
|
||||
'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequest.h',
|
||||
}],
|
||||
|
||||
'XMLHttpRequestEventTarget': [
|
||||
|
@ -378,7 +376,6 @@ DOMInterfaces = {
|
|||
{
|
||||
'workers': True,
|
||||
'concrete': False,
|
||||
'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestEventTarget.h'
|
||||
}],
|
||||
|
||||
'XMLHttpRequestUpload': [
|
||||
|
@ -389,7 +386,6 @@ DOMInterfaces = {
|
|||
},
|
||||
{
|
||||
'workers': True,
|
||||
'headerFile': 'mozilla/dom/workers/bindings/XMLHttpRequestUpload.h'
|
||||
}],
|
||||
|
||||
'WebSocket': [
|
||||
|
|
|
@ -96,7 +96,7 @@ def DOMClass(descriptor):
|
|||
return """{
|
||||
{ %s },
|
||||
%s, %s
|
||||
}""" % (prototypeChainString, toStringBool(descriptor.nativeIsISupports),
|
||||
}""" % (prototypeChainString, toStringBool(descriptor.nativeOwnership == 'nsisupports'),
|
||||
nativeHooks)
|
||||
|
||||
class CGDOMJSClass(CGThing):
|
||||
|
@ -634,10 +634,58 @@ class CGAddPropertyHook(CGAbstractClassHook):
|
|||
# FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=774279
|
||||
# Using a real trace hook might enable us to deal with non-nsISupports
|
||||
# wrappercached things here.
|
||||
assert self.descriptor.nativeIsISupports
|
||||
assert self.descriptor.nativeOwnership == 'nsisupports'
|
||||
return """ nsContentUtils::PreserveWrapper(reinterpret_cast<nsISupports*>(self), self);
|
||||
return true;"""
|
||||
|
||||
def DeferredFinalizeSmartPtr(descriptor):
|
||||
if descriptor.nativeOwnership == 'owned':
|
||||
smartPtr = 'nsAutoPtr<%s>'
|
||||
else:
|
||||
assert descriptor.nativeOwnership == 'refcounted'
|
||||
smartPtr = 'nsRefPtr<%s>'
|
||||
return smartPtr % descriptor.nativeType
|
||||
|
||||
class CGDeferredFinalizePointers(CGThing):
|
||||
def __init__(self, descriptor):
|
||||
CGThing.__init__(self)
|
||||
self.descriptor = descriptor
|
||||
|
||||
def declare(self):
|
||||
return ""
|
||||
|
||||
def define(self):
|
||||
return """nsTArray<%s >* sDeferredFinalizePointers;
|
||||
""" % DeferredFinalizeSmartPtr(self.descriptor)
|
||||
|
||||
class CGGetDeferredFinalizePointers(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, "GetDeferredFinalizePointers", "void*", [])
|
||||
|
||||
def definition_body(self):
|
||||
return """ nsTArray<%s >* pointers = sDeferredFinalizePointers;
|
||||
sDeferredFinalizePointers = nullptr;
|
||||
return pointers;""" % DeferredFinalizeSmartPtr(self.descriptor)
|
||||
|
||||
class CGDeferredFinalize(CGAbstractStaticMethod):
|
||||
def __init__(self, descriptor):
|
||||
CGAbstractStaticMethod.__init__(self, descriptor, "DeferredFinalize", "bool", [Argument('int32_t', 'slice'), Argument('void*', 'data')])
|
||||
|
||||
def definition_body(self):
|
||||
smartPtr = DeferredFinalizeSmartPtr(self.descriptor)
|
||||
return """ nsTArray<%(smartPtr)s >* pointers = static_cast<nsTArray<%(smartPtr)s >*>(data);
|
||||
uint32_t oldLen = pointers->Length();
|
||||
if (slice == -1 || slice > oldLen) {
|
||||
slice = oldLen;
|
||||
}
|
||||
uint32_t newLen = oldLen - slice;
|
||||
pointers->RemoveElementsAt(newLen, slice);
|
||||
if (newLen == 0) {
|
||||
delete pointers;
|
||||
return true;
|
||||
}
|
||||
return false;""" % { 'smartPtr': smartPtr }
|
||||
|
||||
def finalizeHook(descriptor, hookName, context):
|
||||
if descriptor.customFinalize:
|
||||
return """if (self) {
|
||||
|
@ -646,14 +694,36 @@ def finalizeHook(descriptor, hookName, context):
|
|||
clearWrapper = "ClearWrapper(self, self);\n" if descriptor.wrapperCache else ""
|
||||
if descriptor.workers:
|
||||
release = "self->Release();"
|
||||
else:
|
||||
assert descriptor.nativeIsISupports
|
||||
elif descriptor.nativeOwnership == 'nsisupports':
|
||||
release = """XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
||||
if (rt) {
|
||||
rt->DeferredRelease(reinterpret_cast<nsISupports*>(self));
|
||||
} else {
|
||||
NS_RELEASE(self);
|
||||
}"""
|
||||
else:
|
||||
smartPtr = DeferredFinalizeSmartPtr(descriptor)
|
||||
release = """static bool registered = false;
|
||||
if (!registered) {
|
||||
XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
|
||||
if (!rt) {
|
||||
%(smartPtr)s dying;
|
||||
Take(dying, self);
|
||||
return;
|
||||
}
|
||||
rt->RegisterDeferredFinalize(GetDeferredFinalizePointers, DeferredFinalize);
|
||||
registered = true;
|
||||
}
|
||||
if (!sDeferredFinalizePointers) {
|
||||
sDeferredFinalizePointers = new nsAutoTArray<%(smartPtr)s, 16>();
|
||||
}
|
||||
%(smartPtr)s* defer = sDeferredFinalizePointers->AppendElement();
|
||||
if (!defer) {
|
||||
%(smartPtr)s dying;
|
||||
Take(dying, self);
|
||||
return;
|
||||
}
|
||||
Take(*defer, self);""" % { 'smartPtr': smartPtr }
|
||||
return clearWrapper + release
|
||||
|
||||
class CGClassFinalizeHook(CGAbstractClassHook):
|
||||
|
@ -743,6 +813,7 @@ class CGClassHasInstanceHook(CGAbstractStaticMethod):
|
|||
return self.generate_code()
|
||||
|
||||
def generate_code(self):
|
||||
assert self.descriptor.nativeOwnership == 'nsisupports'
|
||||
return """ if (!vp.isObject()) {
|
||||
*bp = false;
|
||||
return true;
|
||||
|
@ -961,7 +1032,7 @@ class MethodDefiner(PropertyDefiner):
|
|||
"flags": "JSPROP_ENUMERATE",
|
||||
"pref": None })
|
||||
|
||||
if not descriptor.interface.parent and not static and not descriptor.workers:
|
||||
if not descriptor.interface.parent and not static and descriptor.nativeOwnership == 'nsisupports':
|
||||
self.chrome.append({"name": 'QueryInterface',
|
||||
"methodInfo": False,
|
||||
"length": 1,
|
||||
|
@ -1385,6 +1456,15 @@ def CreateBindingJSObject(descriptor, parent):
|
|||
}
|
||||
|
||||
js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(aObject));
|
||||
"""
|
||||
if descriptor.nativeOwnership in ['refcounted', 'nsisupports']:
|
||||
create += """ NS_ADDREF(aObject);
|
||||
"""
|
||||
else:
|
||||
assert descriptor.nativeOwnership == 'owned'
|
||||
create += """ // Make sure the native objects inherit from NonRefcountedDOMObject so that we
|
||||
// log their ctor and dtor.
|
||||
MustInheritFromNonRefcountedDOMObject(aObject);
|
||||
"""
|
||||
return create % parent
|
||||
|
||||
|
@ -1418,7 +1498,6 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
|
|||
}
|
||||
|
||||
%s
|
||||
NS_ADDREF(aObject);
|
||||
|
||||
aCache->SetWrapper(obj);
|
||||
|
||||
|
@ -1453,7 +1532,6 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
|
|||
}
|
||||
|
||||
%s
|
||||
NS_ADDREF(aObject);
|
||||
|
||||
return obj;""" % CreateBindingJSObject(self.descriptor, "global")
|
||||
|
||||
|
@ -3902,10 +3980,7 @@ class CGUnionStruct(CGThing):
|
|||
${externalType} GetAs${name}() const
|
||||
{
|
||||
MOZ_ASSERT(Is${name}(), "Wrong type!");
|
||||
// The cast to ${externalType} is needed to work around a bug in Apple's
|
||||
// clang compiler, for some reason it doesn't call |S::operator T&| when
|
||||
// casting S<T> to T& and T is forward declared.
|
||||
return (${externalType})mValue.m${name}.Value();
|
||||
return const_cast<${structType}&>(mValue.m${name}.Value());
|
||||
}
|
||||
${structType}& SetAs${name}()
|
||||
{
|
||||
|
@ -4787,7 +4862,6 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
|
|||
" if (found) {\n"
|
||||
" return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
|
||||
" }\n" +
|
||||
" return true;\n"
|
||||
"}\n") % (self.descriptor.nativeType, self.descriptor.name)
|
||||
return set + """return mozilla::dom::DOMProxyHandler::defineProperty(%s);""" % ", ".join(a.name for a in self.args)
|
||||
|
||||
|
@ -5105,17 +5179,23 @@ class CGDescriptor(CGThing):
|
|||
if hasLenientSetter: cgThings.append(CGGenericSetter(descriptor,
|
||||
lenientThis=True))
|
||||
|
||||
if descriptor.concrete and not descriptor.proxy:
|
||||
if not descriptor.workers and descriptor.wrapperCache:
|
||||
cgThings.append(CGAddPropertyHook(descriptor))
|
||||
if descriptor.concrete:
|
||||
if descriptor.nativeOwnership == 'owned' or descriptor.nativeOwnership == 'refcounted':
|
||||
cgThings.append(CGDeferredFinalizePointers(descriptor))
|
||||
cgThings.append(CGGetDeferredFinalizePointers(descriptor))
|
||||
cgThings.append(CGDeferredFinalize(descriptor))
|
||||
|
||||
# Always have a finalize hook, regardless of whether the class wants a
|
||||
# custom hook.
|
||||
cgThings.append(CGClassFinalizeHook(descriptor))
|
||||
if not descriptor.proxy:
|
||||
if not descriptor.workers and descriptor.wrapperCache:
|
||||
cgThings.append(CGAddPropertyHook(descriptor))
|
||||
|
||||
# Only generate a trace hook if the class wants a custom hook.
|
||||
if (descriptor.customTrace):
|
||||
cgThings.append(CGClassTraceHook(descriptor))
|
||||
# Always have a finalize hook, regardless of whether the class
|
||||
# wants a custom hook.
|
||||
cgThings.append(CGClassFinalizeHook(descriptor))
|
||||
|
||||
# Only generate a trace hook if the class wants a custom hook.
|
||||
if (descriptor.customTrace):
|
||||
cgThings.append(CGClassTraceHook(descriptor))
|
||||
|
||||
if descriptor.interface.hasInterfaceObject():
|
||||
cgThings.append(CGClassConstructHook(descriptor))
|
||||
|
@ -5608,7 +5688,8 @@ class CGBindingRoot(CGThing):
|
|||
['mozilla/dom/BindingUtils.h',
|
||||
'mozilla/dom/DOMJSClass.h',
|
||||
'mozilla/dom/DOMJSProxyHandler.h'],
|
||||
['mozilla/dom/Nullable.h',
|
||||
['mozilla/dom/NonRefcountedDOMObject.h',
|
||||
'mozilla/dom/Nullable.h',
|
||||
'PrimitiveConversions.h',
|
||||
'XPCQuickStubs.h',
|
||||
'nsDOMQS.h',
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from WebIDL import IDLInterface, IDLExternalInterface
|
||||
|
||||
autogenerated_comment = "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n"
|
||||
|
||||
class Configuration:
|
||||
|
@ -23,14 +25,31 @@ class Configuration:
|
|||
self.interfaces = {}
|
||||
self.maxProtoChainLength = 0;
|
||||
for thing in parseData:
|
||||
if not thing.isInterface(): continue
|
||||
# Some toplevel things are sadly types, and those have an
|
||||
# isInterface that doesn't mean the same thing as IDLObject's
|
||||
# isInterface()...
|
||||
if (not isinstance(thing, IDLInterface) and
|
||||
not isinstance(thing, IDLExternalInterface)):
|
||||
continue
|
||||
iface = thing
|
||||
if iface.identifier.name not in config: continue
|
||||
self.interfaces[iface.identifier.name] = iface
|
||||
entry = config[iface.identifier.name]
|
||||
if iface.identifier.name not in config:
|
||||
# Completely skip consequential interfaces with no descriptor
|
||||
# because chances are we don't need to do anything interesting
|
||||
# with them.
|
||||
if iface.isConsequential():
|
||||
continue
|
||||
entry = {}
|
||||
else:
|
||||
entry = config[iface.identifier.name]
|
||||
if not isinstance(entry, list):
|
||||
assert isinstance(entry, dict)
|
||||
entry = [entry]
|
||||
elif len(entry) == 1 and entry[0].get("workers", False):
|
||||
# List with only a workers descriptor means we should
|
||||
# infer a mainthread descriptor. If you want only
|
||||
# workers bindings, don't use a list here.
|
||||
entry.append({})
|
||||
self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
|
||||
|
||||
# Mark the descriptors for which only a single nativeType implements
|
||||
|
@ -146,8 +165,11 @@ class Descriptor(DescriptorProvider):
|
|||
if self.nativeType == "JSObject":
|
||||
headerDefault = "jsapi.h"
|
||||
else:
|
||||
headerDefault = self.nativeType
|
||||
headerDefault = headerDefault.replace("::", "/") + ".h"
|
||||
if self.workers:
|
||||
headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
|
||||
else:
|
||||
headerDefault = self.nativeType
|
||||
headerDefault = headerDefault.replace("::", "/") + ".h"
|
||||
self.headerFile = desc.get('headerFile', headerDefault)
|
||||
|
||||
if self.interface.isCallback() or self.interface.isExternal():
|
||||
|
@ -227,7 +249,18 @@ class Descriptor(DescriptorProvider):
|
|||
self.interface.identifier.name)
|
||||
self.prefable = desc.get('prefable', False)
|
||||
|
||||
self.nativeIsISupports = not self.workers
|
||||
if self.workers:
|
||||
if desc.get('nativeOwnership', 'worker') != 'worker':
|
||||
raise TypeError("Worker descriptor for %s should have 'worker' "
|
||||
"as value for nativeOwnership" %
|
||||
self.interface.identifier.name)
|
||||
self.nativeOwnership = "worker"
|
||||
else:
|
||||
self.nativeOwnership = desc.get('nativeOwnership', 'nsisupports')
|
||||
if not self.nativeOwnership in ['owned', 'refcounted', 'nsisupports']:
|
||||
raise TypeError("Descriptor for %s has unrecognized value (%s) "
|
||||
"for nativeOwnership" %
|
||||
(self.interface.identifier.name, self.nativeOwnership))
|
||||
self.customTrace = desc.get('customTrace', self.workers)
|
||||
self.customFinalize = desc.get('customFinalize', self.workers)
|
||||
self.wrapperCache = self.workers or desc.get('wrapperCache', True)
|
||||
|
|
|
@ -10,7 +10,7 @@ props = ""
|
|||
for [prop, pref] in propList:
|
||||
extendedAttrs = ["Throws", "TreatNullAs=EmptyString"]
|
||||
if pref is not "":
|
||||
extendedAttrs.append("Pref=%s" % pref)
|
||||
extendedAttrs.append('Pref="%s"' % pref)
|
||||
if not prop.startswith("Moz"):
|
||||
prop = prop[0].lower() + prop[1:]
|
||||
# Unfortunately, even some of the getters here are fallible
|
||||
|
|
|
@ -58,6 +58,7 @@ EXPORTS_$(binding_include_path) = \
|
|||
DOMJSClass.h \
|
||||
DOMJSProxyHandler.h \
|
||||
Errors.msg \
|
||||
NonRefcountedDOMObject.h \
|
||||
Nullable.h \
|
||||
PrimitiveConversions.h \
|
||||
PrototypeList.h \
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
|
||||
/* vim: set ts=2 sw=2 et tw=79: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_dom_NonRefcountedDOMObject_h__
|
||||
#define mozilla_dom_NonRefcountedDOMObject_h__
|
||||
|
||||
#include "nsTraceRefcnt.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Natives for DOM classes with 'owned' as the value for nativeOwnership in
|
||||
// Bindings.conf need to inherit from this class.
|
||||
// If you're seeing objects of this class leak then natives for one of the DOM
|
||||
// classes with 'owned' as the value for nativeOwnership in Bindings.conf is
|
||||
// leaking. If the native for that class has MOZ_COUNT_CTOR/DTOR in its
|
||||
// constructor/destructor then it should show up in the leak log too.
|
||||
class NonRefcountedDOMObject
|
||||
{
|
||||
protected:
|
||||
NonRefcountedDOMObject()
|
||||
{
|
||||
MOZ_COUNT_CTOR(NonRefcountedDOMObject);
|
||||
}
|
||||
~NonRefcountedDOMObject()
|
||||
{
|
||||
MOZ_COUNT_DTOR(NonRefcountedDOMObject);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_NonRefcountedDOMObject_h__ */
|
|
@ -1433,7 +1433,9 @@ class IDLTypedefType(IDLType, IDLObjectWithIdentifier):
|
|||
|
||||
def finish(self, parentScope):
|
||||
# Maybe the IDLObjectWithIdentifier for the typedef should be
|
||||
# a separate thing from the type?
|
||||
# a separate thing from the type? If that happens, we can
|
||||
# remove some hackery around avoiding isInterface() in
|
||||
# Configuration.py.
|
||||
self.complete(parentScope)
|
||||
|
||||
def validate(self):
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "nsXPCOMCIDInternal.h"
|
||||
|
||||
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/LazyIdleThread.h"
|
||||
#include "mozilla/Util.h"
|
||||
|
||||
|
@ -276,9 +277,7 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
|
|||
nsCOMPtr<nsIDOMBluetoothDeviceEvent> e = do_QueryInterface(event);
|
||||
e->InitBluetoothDeviceEvent(NS_LITERAL_STRING("devicefound"),
|
||||
false, false, device);
|
||||
e->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
DispatchTrustedEvent(event);
|
||||
} else if (aData.name().EqualsLiteral("DeviceDisappeared")) {
|
||||
const nsAString& deviceAddress = aData.value().get_nsString();
|
||||
|
||||
|
@ -288,9 +287,7 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
|
|||
nsCOMPtr<nsIDOMBluetoothDeviceAddressEvent> e = do_QueryInterface(event);
|
||||
e->InitBluetoothDeviceAddressEvent(NS_LITERAL_STRING("devicedisappeared"),
|
||||
false, false, deviceAddress);
|
||||
e->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
DispatchTrustedEvent(e);
|
||||
} else if (aData.name().EqualsLiteral("DeviceCreated")) {
|
||||
NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue,
|
||||
"DeviceCreated: Invalid value type");
|
||||
|
@ -304,9 +301,7 @@ BluetoothAdapter::Notify(const BluetoothSignal& aData)
|
|||
nsCOMPtr<nsIDOMBluetoothDeviceEvent> e = do_QueryInterface(event);
|
||||
e->InitBluetoothDeviceEvent(NS_LITERAL_STRING("devicecreated"),
|
||||
false, false, device);
|
||||
e->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
DispatchTrustedEvent(e);
|
||||
} else if (aData.name().EqualsLiteral("PropertyChanged")) {
|
||||
NS_ASSERTION(aData.value().type() == BluetoothValue::TArrayOfBluetoothNamedValue,
|
||||
"PropertyChanged: Invalid value type");
|
||||
|
@ -816,7 +811,37 @@ BluetoothAdapter::SendFile(const nsAString& aDeviceAddress,
|
|||
nsIDOMBlob* aBlob,
|
||||
nsIDOMDOMRequest** aRequest)
|
||||
{
|
||||
// Will implement in another patch
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
NS_WARNING("BluetoothService not available!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMRequestService> rs = do_GetService("@mozilla.org/dom/dom-request-service;1");
|
||||
if (!rs) {
|
||||
NS_WARNING("No DOMRequest Service!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMDOMRequest> req;
|
||||
nsresult rv = rs->CreateRequest(GetOwner(), getter_AddRefs(req));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't create DOMRequest!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
BlobChild* actor =
|
||||
mozilla::dom::ContentChild::GetSingleton()->GetOrCreateActorForBlob(aBlob);
|
||||
|
||||
if (!actor) {
|
||||
NS_WARNING("Can't create actor");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsRefPtr<BluetoothVoidReplyRunnable> result = new BluetoothVoidReplyRunnable(req);
|
||||
bs->SendFile(aDeviceAddress, nullptr, actor, result);
|
||||
req.forget(aRequest);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -200,21 +200,8 @@ BluetoothDevice::Notify(const BluetoothSignal& aData)
|
|||
|
||||
SetPropertyByValue(v);
|
||||
if (name.EqualsLiteral("Connected")) {
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv;
|
||||
if (mConnected) {
|
||||
rv = event->InitEvent(NS_LITERAL_STRING("connected"), false, false);
|
||||
} else {
|
||||
rv = event->InitEvent(NS_LITERAL_STRING("disconnected"), false, false);
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to init the connected/disconnected event!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
DispatchTrustedEvent(mConnected ? NS_LITERAL_STRING("connected")
|
||||
: NS_LITERAL_STRING("disconnected"));
|
||||
} else {
|
||||
nsRefPtr<BluetoothPropertyEvent> e = BluetoothPropertyEvent::Create(name);
|
||||
e->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("propertychanged"));
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsIRadioInterfaceLayer.h"
|
||||
#include "nsISystemMessagesInternal.h"
|
||||
#include "BluetoothUtils.h"
|
||||
|
||||
#include "nsVariant.h"
|
||||
|
||||
#include <unistd.h> /* usleep() */
|
||||
|
@ -144,12 +146,9 @@ BluetoothHfpManager::Get()
|
|||
}
|
||||
|
||||
bool
|
||||
BluetoothHfpManager::BroadcastSystemMessage(const char* aCommand,
|
||||
const int aCommandLength)
|
||||
BluetoothHfpManager::BroadcastSystemMessage(const nsAString& aType,
|
||||
const InfallibleTArray<BluetoothNamedValue>& aData)
|
||||
{
|
||||
nsString type;
|
||||
type.AssignLiteral("bluetooth-dialer-command");
|
||||
|
||||
JSContext* cx = nsContentUtils::GetSafeJSContext();
|
||||
NS_ASSERTION(!::JS_IsExceptionPending(cx),
|
||||
"Shouldn't get here when an exception is pending!");
|
||||
|
@ -161,14 +160,7 @@ BluetoothHfpManager::BroadcastSystemMessage(const char* aCommand,
|
|||
return false;
|
||||
}
|
||||
|
||||
JSString* JsData = JS_NewStringCopyN(cx, aCommand, aCommandLength);
|
||||
if (!JsData) {
|
||||
NS_WARNING("JS_NewStringCopyN is out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
jsval v = STRING_TO_JSVAL(JsData);
|
||||
if (!JS_SetProperty(cx, obj, "command", &v)) {
|
||||
if (!SetJsObject(cx, obj, aData)) {
|
||||
NS_WARNING("Failed to set properties of system message!");
|
||||
return false;
|
||||
}
|
||||
|
@ -181,11 +173,50 @@ BluetoothHfpManager::BroadcastSystemMessage(const char* aCommand,
|
|||
return false;
|
||||
}
|
||||
|
||||
systemMessenger->BroadcastMessage(type, OBJECT_TO_JSVAL(obj));
|
||||
systemMessenger->BroadcastMessage(aType, OBJECT_TO_JSVAL(obj));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::NotifySettings(const bool aConnected)
|
||||
{
|
||||
nsString type, name;
|
||||
BluetoothValue v;
|
||||
InfallibleTArray<BluetoothNamedValue> parameters;
|
||||
type.AssignLiteral("bluetooth-hfp-status-changed");
|
||||
|
||||
name.AssignLiteral("connected");
|
||||
v = aConnected;
|
||||
parameters.AppendElement(BluetoothNamedValue(name, v));
|
||||
|
||||
name.AssignLiteral("address");
|
||||
v = GetAddressFromObjectPath(mDevicePath);
|
||||
parameters.AppendElement(BluetoothNamedValue(name, v));
|
||||
|
||||
if (!BroadcastSystemMessage(type, parameters)) {
|
||||
NS_WARNING("Failed to broadcast system message to dialer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::NotifyDialer(const nsAString& aCommand)
|
||||
{
|
||||
nsString type, name, command;
|
||||
command = aCommand;
|
||||
InfallibleTArray<BluetoothNamedValue> parameters;
|
||||
type.AssignLiteral("bluetooth-dialer-command");
|
||||
|
||||
BluetoothValue v(command);
|
||||
parameters.AppendElement(BluetoothNamedValue(type, v));
|
||||
|
||||
if (!BroadcastSystemMessage(type, parameters)) {
|
||||
NS_WARNING("Failed to broadcast system message to dialer");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
BluetoothHfpManager::HandleVolumeChanged(const nsAString& aData)
|
||||
{
|
||||
|
@ -315,6 +346,8 @@ BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
|||
SendLine("+CIND: 5,5,1,0,0,0,0");
|
||||
SendLine("OK");
|
||||
} else if (!strncmp(msg, "AT+CMER=", 8)) {
|
||||
// SLC establishment
|
||||
NotifySettings(true);
|
||||
SendLine("OK");
|
||||
} else if (!strncmp(msg, "AT+CHLD=?", 9)) {
|
||||
SendLine("+CHLD: (0,1,2,3)");
|
||||
|
@ -349,22 +382,13 @@ BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
|||
|
||||
SendLine("OK");
|
||||
} else if (!strncmp(msg, "AT+BLDN", 7)) {
|
||||
if (!BroadcastSystemMessage("BLDN", 4)) {
|
||||
NS_WARNING("Failed to broadcast system message to dialer");
|
||||
return;
|
||||
}
|
||||
NotifyDialer(NS_LITERAL_STRING("BLDN"));
|
||||
SendLine("OK");
|
||||
} else if (!strncmp(msg, "ATA", 3)) {
|
||||
if (!BroadcastSystemMessage("ATA", 3)) {
|
||||
NS_WARNING("Failed to broadcast system message to dialer");
|
||||
return;
|
||||
}
|
||||
NotifyDialer(NS_LITERAL_STRING("ATA"));
|
||||
SendLine("OK");
|
||||
} else if (!strncmp(msg, "AT+CHUP", 7)) {
|
||||
if (!BroadcastSystemMessage("CHUP", 4)) {
|
||||
NS_WARNING("Failed to broadcast system message to dialer");
|
||||
return;
|
||||
}
|
||||
NotifyDialer(NS_LITERAL_STRING("CHUP"));
|
||||
SendLine("OK");
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
|
@ -407,9 +431,29 @@ BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
|
|||
return NS_FAILED(rv) ? false : true;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothHfpManager::Listen()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
BluetoothService* bs = BluetoothService::Get();
|
||||
if (!bs) {
|
||||
NS_WARNING("BluetoothService not available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = bs->ListenSocketViaService(BluetoothReservedChannels::HANDSFREE_AG,
|
||||
BluetoothSocketType::RFCOMM,
|
||||
true,
|
||||
false,
|
||||
this);
|
||||
return NS_FAILED(rv) ? false : true;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothHfpManager::Disconnect()
|
||||
{
|
||||
NotifySettings(false);
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothReplyRunnable;
|
||||
class BluetoothNamedValue;
|
||||
|
||||
class BluetoothHfpManager : public mozilla::ipc::UnixSocketConsumer
|
||||
, public nsIObserver
|
||||
|
@ -34,13 +35,15 @@ public:
|
|||
bool SendLine(const char* aMessage);
|
||||
void CallStateChanged(int aCallIndex, int aCallState,
|
||||
const char* aNumber, bool aIsActive);
|
||||
|
||||
bool Listen();
|
||||
private:
|
||||
BluetoothHfpManager();
|
||||
|
||||
bool BroadcastSystemMessage(const char* aCommand,
|
||||
const int aCommandLength);
|
||||
nsresult HandleVolumeChanged(const nsAString& aData);
|
||||
bool BroadcastSystemMessage(const nsAString& aType,
|
||||
const InfallibleTArray<BluetoothNamedValue>& aData);
|
||||
void NotifyDialer(const nsAString& aCommand);
|
||||
void NotifySettings(const bool aConnected);
|
||||
|
||||
int mCurrentVgs;
|
||||
int mCurrentCallIndex;
|
||||
|
|
|
@ -127,26 +127,8 @@ private:
|
|||
nsresult
|
||||
BluetoothManager::FireEnabledDisabledEvent(bool aEnabled)
|
||||
{
|
||||
nsString eventName;
|
||||
|
||||
if (aEnabled) {
|
||||
eventName.AssignLiteral("enabled");
|
||||
} else {
|
||||
eventName.AssignLiteral("disabled");
|
||||
}
|
||||
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(eventName, false, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = event->SetTrusted(true);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
bool dummy;
|
||||
rv = DispatchEvent(event, &dummy);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
return DispatchTrustedEvent(aEnabled ? NS_LITERAL_STRING("enabled")
|
||||
: NS_LITERAL_STRING("disabled"));
|
||||
}
|
||||
|
||||
BluetoothManager::BluetoothManager(nsPIDOMWindow *aWindow)
|
||||
|
@ -273,17 +255,7 @@ void
|
|||
BluetoothManager::Notify(const BluetoothSignal& aData)
|
||||
{
|
||||
if (aData.name().EqualsLiteral("AdapterAdded")) {
|
||||
nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nullptr, nullptr);
|
||||
nsresult rv = event->InitEvent(NS_LITERAL_STRING("adapteradded"), false, false);
|
||||
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to init the adapteradded event!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool dummy;
|
||||
DispatchEvent(event, &dummy);
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("adapteradded"));
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
nsCString warningMsg;
|
||||
|
|
|
@ -12,17 +12,85 @@
|
|||
#include "BluetoothServiceUuid.h"
|
||||
#include "ObexBase.h"
|
||||
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsIInputStream.h"
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
static mozilla::RefPtr<BluetoothOppManager> sInstance;
|
||||
static nsCOMPtr<nsIInputStream> stream = nullptr;
|
||||
static uint64_t sSentFileSize = 0;
|
||||
|
||||
class ReadFileTask : public nsRunnable
|
||||
{
|
||||
public:
|
||||
ReadFileTask(nsIDOMBlob* aBlob) : mBlob(aBlob)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
NS_WARNING("Can't read file from main thread");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
uint64_t fileSize;
|
||||
nsresult rv = mBlob->GetSize(&fileSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't get file size");
|
||||
return NS_ERROR_FAILURE;;
|
||||
}
|
||||
|
||||
if (stream == nullptr) {
|
||||
rv = mBlob->GetInternalStream(getter_AddRefs(stream));
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't get internal stream of blob");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 255 is the Minimum OBEX Packet Length (See section 3.3.1.4,
|
||||
* IrOBEX ver 1.2)
|
||||
*/
|
||||
char buf[255];
|
||||
uint32_t numRead;
|
||||
int offset = 0;
|
||||
|
||||
// function inputstream->Read() only works on non-main thread
|
||||
rv = stream->Read(buf, sizeof(buf), &numRead);
|
||||
if (NS_FAILED(rv)) {
|
||||
// Needs error handling here
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (numRead > 0) {
|
||||
if (sSentFileSize + numRead >= fileSize) {
|
||||
sInstance->SendPutRequest((uint8_t*)buf, numRead, true);
|
||||
} else {
|
||||
sInstance->SendPutRequest((uint8_t*)buf, numRead, false);
|
||||
}
|
||||
|
||||
sSentFileSize += numRead;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
};
|
||||
|
||||
BluetoothOppManager::BluetoothOppManager() : mConnected(false)
|
||||
, mConnectionId(1)
|
||||
, mLastCommand(0)
|
||||
, mBlob(nullptr)
|
||||
, mRemoteObexVersion(0)
|
||||
, mRemoteConnectionFlags(0)
|
||||
, mRemoteMaxPacketLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -79,10 +147,24 @@ BluetoothOppManager::Disconnect()
|
|||
}
|
||||
|
||||
bool
|
||||
BluetoothOppManager::SendFile(BlobParent* aParent,
|
||||
BluetoothOppManager::SendFile(BlobParent* aActor,
|
||||
BluetoothReplyRunnable* aRunnable)
|
||||
{
|
||||
// will implement in another patch.
|
||||
if (mBlob) {
|
||||
// Means there's a sending process. Reply error.
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process of sending a file:
|
||||
* - Keep blob because OPP connection has not been established yet.
|
||||
* - Create an OPP connection by SendConnectRequest()
|
||||
* - After receiving the response, start to read file and send.
|
||||
*/
|
||||
mBlob = aActor->GetBlob();
|
||||
|
||||
SendConnectRequest();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -98,21 +180,83 @@ void
|
|||
BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
|
||||
{
|
||||
uint8_t responseCode = aMessage->mData[0];
|
||||
int packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
|
||||
int receivedLength = aMessage->mSize;
|
||||
|
||||
if (mLastCommand == ObexRequestCode::Connect) {
|
||||
if (responseCode == ObexResponseCode::Success) {
|
||||
mConnected = true;
|
||||
|
||||
// Keep remote information
|
||||
mRemoteObexVersion = aMessage->mData[3];
|
||||
mRemoteConnectionFlags = aMessage->mData[4];
|
||||
mRemoteMaxPacketLength =
|
||||
(((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
|
||||
|
||||
if (mBlob) {
|
||||
/*
|
||||
* Before sending content, we have to send a header including
|
||||
* information such as file name, file length and content type.
|
||||
*/
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
|
||||
nsString fileName;
|
||||
if (file) {
|
||||
rv = file->GetName(fileName);
|
||||
}
|
||||
|
||||
if (!file || fileName.IsEmpty()) {
|
||||
fileName.AssignLiteral("Unknown");
|
||||
}
|
||||
|
||||
uint64_t fileSize;
|
||||
rv = mBlob->GetSize(&fileSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Can't get file size");
|
||||
return;
|
||||
}
|
||||
|
||||
sSentFileSize = 0;
|
||||
sInstance->SendPutHeaderRequest(fileName, fileSize);
|
||||
}
|
||||
}
|
||||
} else if (mLastCommand == ObexRequestCode::Disconnect) {
|
||||
if (responseCode == ObexResponseCode::Success) {
|
||||
if (responseCode != ObexResponseCode::Success) {
|
||||
// FIXME: Needs error handling here
|
||||
NS_WARNING("[OPP] Disconnect failed");
|
||||
} else {
|
||||
mConnected = false;
|
||||
mBlob = nullptr;
|
||||
}
|
||||
} else if (mLastCommand == ObexRequestCode::Put) {
|
||||
if (responseCode != ObexResponseCode::Continue) {
|
||||
// FIXME: Needs error handling here
|
||||
NS_WARNING("[OPP] Put failed");
|
||||
} else {
|
||||
nsCOMPtr<nsIThread> t;
|
||||
NS_NewThread(getter_AddRefs(t));
|
||||
|
||||
nsRefPtr<ReadFileTask> task = new ReadFileTask(mBlob);
|
||||
|
||||
if (NS_FAILED(t->Dispatch(task, NS_DISPATCH_NORMAL))) {
|
||||
NS_WARNING("Cannot dispatch ring task!");
|
||||
}
|
||||
}
|
||||
} else if (mLastCommand == ObexRequestCode::PutFinal) {
|
||||
if (responseCode != ObexResponseCode::Success) {
|
||||
// FIXME: Needs error handling here
|
||||
NS_WARNING("[OPP] PutFinal failed");
|
||||
} else {
|
||||
SendDisconnectRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::SendConnectReqeust()
|
||||
BluetoothOppManager::SendConnectRequest()
|
||||
{
|
||||
if (mConnected) return;
|
||||
|
||||
// Section 3.3.1 "Connect", IrOBEX 1.2
|
||||
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2][Headers:var]
|
||||
uint8_t req[255];
|
||||
|
@ -123,7 +267,7 @@ BluetoothOppManager::SendConnectReqeust()
|
|||
req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
|
||||
req[6] = BluetoothOppManager::MAX_PACKET_LENGTH;
|
||||
|
||||
index += AppendHeaderConnectionId(&req[index], mConnectionId++);
|
||||
index += AppendHeaderConnectionId(&req[index], mConnectionId);
|
||||
SetObexPacketInfo(req, ObexRequestCode::Connect, index);
|
||||
mLastCommand = ObexRequestCode::Connect;
|
||||
|
||||
|
@ -132,3 +276,86 @@ BluetoothOppManager::SendConnectReqeust()
|
|||
SendSocketData(s);
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName, int aFileSize)
|
||||
{
|
||||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
const PRUnichar* fileNamePtr = aFileName.BeginReading();
|
||||
uint32_t len = aFileName.Length();
|
||||
uint8_t* fileName = new uint8_t[(len + 1) * 2];
|
||||
for (int i = 0; i < len; i++) {
|
||||
fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
|
||||
fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
|
||||
}
|
||||
|
||||
fileName[len * 2] = 0x00;
|
||||
fileName[len * 2 + 1] = 0x00;
|
||||
|
||||
int index = 3;
|
||||
index += AppendHeaderConnectionId(&req[index], mConnectionId);
|
||||
index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
|
||||
index += AppendHeaderLength(&req[index], aFileSize);
|
||||
|
||||
SetObexPacketInfo(req, ObexRequestCode::Put, index);
|
||||
mLastCommand = ObexRequestCode::Put;
|
||||
|
||||
UnixSocketRawData* s = new UnixSocketRawData(index);
|
||||
memcpy(s->mData, req, s->mSize);
|
||||
SendSocketData(s);
|
||||
|
||||
delete [] fileName;
|
||||
delete [] req;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
|
||||
int aFileBodyLength,
|
||||
bool aFinal)
|
||||
{
|
||||
int sentFileBodyLength = 0;
|
||||
int index = 3;
|
||||
int packetLeftSpace = mRemoteMaxPacketLength - index - 3;
|
||||
|
||||
if (!mConnected) return;
|
||||
if (aFileBodyLength > packetLeftSpace) {
|
||||
NS_WARNING("Not allowed such a small MaxPacketLength value");
|
||||
return;
|
||||
}
|
||||
|
||||
// IrOBEX 1.2 3.3.3
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
|
||||
|
||||
index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
|
||||
|
||||
if (aFinal) {
|
||||
SetObexPacketInfo(req, ObexRequestCode::PutFinal, index);
|
||||
mLastCommand = ObexRequestCode::PutFinal;
|
||||
} else {
|
||||
SetObexPacketInfo(req, ObexRequestCode::Put, index);
|
||||
mLastCommand = ObexRequestCode::Put;
|
||||
}
|
||||
|
||||
UnixSocketRawData* s = new UnixSocketRawData(index);
|
||||
memcpy(s->mData, req, s->mSize);
|
||||
SendSocketData(s);
|
||||
|
||||
delete [] req;
|
||||
}
|
||||
|
||||
void
|
||||
BluetoothOppManager::SendDisconnectRequest()
|
||||
{
|
||||
// IrOBEX 1.2 3.3.2
|
||||
// [opcode:1][length:2][Headers:var]
|
||||
uint8_t req[255];
|
||||
int index = 3;
|
||||
|
||||
SetObexPacketInfo(req, ObexRequestCode::Disconnect, index);
|
||||
mLastCommand = ObexRequestCode::Disconnect;
|
||||
|
||||
UnixSocketRawData* s = new UnixSocketRawData(index);
|
||||
memcpy(s->mData, req, s->mSize);
|
||||
SendSocketData(s);
|
||||
}
|
||||
|
|
|
@ -8,12 +8,13 @@
|
|||
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "mozilla/ipc/UnixSocket.h"
|
||||
#include "nsIDOMFile.h"
|
||||
|
||||
BEGIN_BLUETOOTH_NAMESPACE
|
||||
|
||||
class BluetoothReplyRunnable;
|
||||
class BlobParent;
|
||||
|
||||
class BluetoothOppManager : public mozilla::ipc::UnixSocketConsumer
|
||||
{
|
||||
|
@ -50,13 +51,24 @@ public:
|
|||
|
||||
bool StopSendingFile(BluetoothReplyRunnable* aRunnable);
|
||||
|
||||
// xxx For runnable use
|
||||
void SendConnectRequest();
|
||||
void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
|
||||
void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength,
|
||||
bool aFinal);
|
||||
void SendDisconnectRequest();
|
||||
|
||||
private:
|
||||
BluetoothOppManager();
|
||||
void SendConnectReqeust();
|
||||
|
||||
bool mConnected;
|
||||
int mConnectionId;
|
||||
int mLastCommand;
|
||||
uint8_t mRemoteObexVersion;
|
||||
uint8_t mRemoteConnectionFlags;
|
||||
int mRemoteMaxPacketLength;
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> mBlob;
|
||||
};
|
||||
|
||||
END_BLUETOOTH_NAMESPACE
|
||||
|
|
|
@ -25,8 +25,8 @@ public:
|
|||
NS_IMPL_ISUPPORTS1(BluetoothRILTelephonyCallback, nsIRILTelephonyCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothRILTelephonyCallback::CallStateChanged(PRUint32 aCallIndex,
|
||||
PRUint16 aCallState,
|
||||
BluetoothRILTelephonyCallback::CallStateChanged(uint32_t aCallIndex,
|
||||
uint16_t aCallState,
|
||||
const nsAString& aNumber,
|
||||
bool aIsActive)
|
||||
{
|
||||
|
@ -38,8 +38,8 @@ BluetoothRILTelephonyCallback::CallStateChanged(PRUint32 aCallIndex,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothRILTelephonyCallback::EnumerateCallState(PRUint32 aCallIndex,
|
||||
PRUint16 aCallState,
|
||||
BluetoothRILTelephonyCallback::EnumerateCallState(uint32_t aCallIndex,
|
||||
uint16_t aCallState,
|
||||
const nsAString_internal& aNumber,
|
||||
bool aIsActive,
|
||||
bool* aResult)
|
||||
|
@ -49,7 +49,7 @@ BluetoothRILTelephonyCallback::EnumerateCallState(PRUint32 aCallIndex,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
BluetoothRILTelephonyCallback::NotifyError(PRInt32 aCallIndex,
|
||||
BluetoothRILTelephonyCallback::NotifyError(int32_t aCallIndex,
|
||||
const nsAString& aError)
|
||||
{
|
||||
return NS_OK;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#define mozilla_dom_bluetooth_bluetootheventservice_h__
|
||||
|
||||
#include "BluetoothCommon.h"
|
||||
#include "mozilla/dom/ipc/Blob.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
|
@ -279,6 +280,19 @@ public:
|
|||
virtual void
|
||||
DisconnectObjectPush(BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual bool
|
||||
SendFile(const nsAString& aDeviceAddress,
|
||||
BlobParent* aBlobParent,
|
||||
BlobChild* aBlobChild,
|
||||
BluetoothReplyRunnable* aRunnable) = 0;
|
||||
|
||||
virtual nsresult
|
||||
ListenSocketViaService(int aChannel,
|
||||
BluetoothSocketType aType,
|
||||
bool aAuth,
|
||||
bool aEncrypt,
|
||||
mozilla::ipc::UnixSocketConsumer* aConsumer) = 0;
|
||||
|
||||
bool
|
||||
IsEnabled() const
|
||||
{
|
||||
|
|
|
@ -30,6 +30,21 @@ namespace BluetoothServiceUuidStr {
|
|||
static const char* ObjectPush = "00001105-0000-1000-8000-00805F9B34FB";
|
||||
}
|
||||
|
||||
// TODO/qdot: Move these back into gonk and make the service handler deal with
|
||||
// it there.
|
||||
//
|
||||
// Gotten from reading the "u8" values in B2G/external/bluez/src/adapter.c
|
||||
// These were hardcoded into android
|
||||
enum BluetoothReservedChannels {
|
||||
DIALUP_NETWORK = 1,
|
||||
HANDSFREE_AG = 10,
|
||||
HEADSET_AG = 11,
|
||||
OPUSH = 12,
|
||||
SIM_ACCESS = 15,
|
||||
PBAP_PSE = 19,
|
||||
FTP = 20,
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,15 @@
|
|||
#include "BluetoothUnixSocketConnector.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#undef LOG
|
||||
#if defined(MOZ_WIDGET_GONK)
|
||||
#include <android/log.h>
|
||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GonkDBus", args);
|
||||
#else
|
||||
#define BTDEBUG true
|
||||
#define LOG(args...) if (BTDEBUG) printf(args);
|
||||
#endif
|
||||
|
||||
USING_BLUETOOTH_NAMESPACE
|
||||
|
||||
static const int RFCOMM_SO_SNDBUF = 70 * 1024; // 70 KB send buffer
|
||||
|
@ -62,33 +71,11 @@ BluetoothUnixSocketConnector::BluetoothUnixSocketConnector(
|
|||
{
|
||||
}
|
||||
|
||||
int
|
||||
BluetoothUnixSocketConnector::Create()
|
||||
bool
|
||||
BluetoothUnixSocketConnector::Setup(int aFd)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
int lm = 0;
|
||||
int fd = -1;
|
||||
int sndbuf;
|
||||
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
break;
|
||||
case BluetoothSocketType::SCO:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
|
||||
break;
|
||||
case BluetoothSocketType::L2CAP:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
NS_WARNING("Could not open bluetooth socket!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* kernel does not yet support LM for SCO */
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
|
@ -104,72 +91,91 @@ BluetoothUnixSocketConnector::Create()
|
|||
}
|
||||
|
||||
if (lm) {
|
||||
if (setsockopt(fd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
|
||||
if (setsockopt(aFd, SOL_RFCOMM, RFCOMM_LM, &lm, sizeof(lm))) {
|
||||
NS_WARNING("setsockopt(RFCOMM_LM) failed, throwing");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mType == BluetoothSocketType::RFCOMM) {
|
||||
sndbuf = RFCOMM_SO_SNDBUF;
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
|
||||
if (setsockopt(aFd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf))) {
|
||||
NS_WARNING("setsockopt(SO_SNDBUF) failed, throwing");
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
BluetoothUnixSocketConnector::Create()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
int fd = -1;
|
||||
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
|
||||
break;
|
||||
case BluetoothSocketType::SCO:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
|
||||
break;
|
||||
case BluetoothSocketType::L2CAP:
|
||||
fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
|
||||
break;
|
||||
default:
|
||||
MOZ_NOT_REACHED();
|
||||
}
|
||||
|
||||
if (fd < 0) {
|
||||
NS_WARNING("Could not open bluetooth socket!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!Setup(fd)) {
|
||||
NS_WARNING("Could not set up socket!");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
bool
|
||||
BluetoothUnixSocketConnector::ConnectInternal(int aFd, const char* aAddress)
|
||||
void
|
||||
BluetoothUnixSocketConnector::CreateAddr(bool aIsServer,
|
||||
socklen_t& aAddrSize,
|
||||
struct sockaddr* aAddr,
|
||||
const char* aAddress)
|
||||
{
|
||||
int n = 1;
|
||||
setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));
|
||||
|
||||
socklen_t addr_sz;
|
||||
struct sockaddr *addr;
|
||||
bdaddr_t bd_address_obj;
|
||||
// Set to BDADDR_ANY, if it's not a server, we'll reset.
|
||||
bdaddr_t bd_address_obj = {{0, 0, 0, 0, 0, 0}};
|
||||
|
||||
if (get_bdaddr(aAddress, &bd_address_obj)) {
|
||||
NS_WARNING("Can't get bluetooth address!");
|
||||
return false;
|
||||
if (!aIsServer && aAddress && strlen(aAddress) > 0) {
|
||||
if (get_bdaddr(aAddress, &bd_address_obj)) {
|
||||
NS_WARNING("Can't get bluetooth address!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (mType) {
|
||||
case BluetoothSocketType::RFCOMM:
|
||||
struct sockaddr_rc addr_rc;
|
||||
addr = (struct sockaddr *)&addr_rc;
|
||||
addr_sz = sizeof(addr_rc);
|
||||
|
||||
memset(addr, 0, addr_sz);
|
||||
aAddrSize = sizeof(addr_rc);
|
||||
memset(aAddr, 0, aAddrSize);
|
||||
addr_rc.rc_family = AF_BLUETOOTH;
|
||||
addr_rc.rc_channel = mChannel;
|
||||
memcpy(&addr_rc.rc_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
|
||||
memcpy(aAddr, &addr_rc, sizeof(addr_rc));
|
||||
break;
|
||||
case BluetoothSocketType::SCO:
|
||||
struct sockaddr_sco addr_sco;
|
||||
addr = (struct sockaddr *)&addr_sco;
|
||||
addr_sz = sizeof(addr_sco);
|
||||
aAddrSize = sizeof(addr_sco);
|
||||
|
||||
memset(addr, 0, addr_sz);
|
||||
memset(aAddr, 0, aAddrSize);
|
||||
addr_sco.sco_family = AF_BLUETOOTH;
|
||||
memcpy(&addr_sco.sco_bdaddr, &bd_address_obj, sizeof(bdaddr_t));
|
||||
memcpy(aAddr, &addr_sco, sizeof(addr_sco));
|
||||
break;
|
||||
default:
|
||||
NS_WARNING("Socket type unknown!");
|
||||
return false;
|
||||
}
|
||||
|
||||
int ret = connect(aFd, addr, addr_sz);
|
||||
|
||||
if (ret) {
|
||||
#if DEBUG
|
||||
//LOG("Socket connect errno=%d\n", errno);
|
||||
#endif
|
||||
NS_WARNING("Socket connect error!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче