This commit is contained in:
Richard Newman 2012-10-01 22:28:32 -07:00
Родитель baea28b72f 2afe4fdad9
Коммит cea016db0d
2056 изменённых файлов: 30825 добавлений и 65035 удалений

Просмотреть файл

@ -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;
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше