merge mozilla-inbound to mozilla-central

This commit is contained in:
Carsten "Tomcat" Book 2014-03-05 13:04:39 +01:00
commit 9e48d03b18
561 changed files with 9049 additions and 3275 deletions

View File

@ -15,9 +15,9 @@
#include "Logging.h"
#endif
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsCURILoader.h"
#include "nsDocShellLoadTypes.h"
#include "nsDOMEvent.h"
#include "nsIChannel.h"
#include "nsIDOMDocument.h"
#include "nsEventListenerManager.h"

View File

@ -28,6 +28,7 @@
#include "nsIAccessibleRelation.h"
#include "nsIDocShellTreeItem.h"
#include "nsIDocShellTreeOwner.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventTarget.h"
#include "nsIDOMDataContainerEvent.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
@ -39,7 +40,6 @@
#include "nsIWebBrowserChrome.h"
#include "nsReadableUtils.h"
#include "nsFocusManager.h"
#include "nsDOMEvent.h"
#ifdef MOZ_XUL
#include "nsIXULDocument.h"
@ -234,7 +234,7 @@ NS_IMETHODIMP
RootAccessible::HandleEvent(nsIDOMEvent* aDOMEvent)
{
MOZ_ASSERT(aDOMEvent);
nsDOMEvent* event = aDOMEvent->InternalDOMEvent();
Event* event = aDOMEvent->InternalDOMEvent();
nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
if (!origTargetNode)
return NS_OK;
@ -266,7 +266,7 @@ void
RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
{
MOZ_ASSERT(aDOMEvent);
nsDOMEvent* event = aDOMEvent->InternalDOMEvent();
Event* event = aDOMEvent->InternalDOMEvent();
nsCOMPtr<nsINode> origTargetNode = do_QueryInterface(event->GetOriginalTarget());
nsAutoString eventType;

View File

@ -309,7 +309,7 @@ this.AccessFu = {
case 'Accessibility:Focus':
this._focused = JSON.parse(aData);
if (this._focused) {
this.showCurrent(true);
this.autoMove({ forcePresent: true, noOpIfOnScreen: true });
}
break;
case 'Accessibility:MoveByGranularity':
@ -353,10 +353,11 @@ this.AccessFu = {
// We delay this for half a second so the awesomebar could close,
// and we could use the current coordinates for the content item.
// XXX TODO figure out how to avoid magic wait here.
Utils.win.setTimeout(
function () {
this.showCurrent(false);
}.bind(this), 500);
this.autoMove({
delay: 500,
forcePresent: true,
noOpIfOnScreen: true,
moveMethod: 'moveFirst' });
}
break;
}
@ -372,9 +373,9 @@ this.AccessFu = {
}
},
showCurrent: function showCurrent(aMove) {
autoMove: function autoMove(aOptions) {
let mm = Utils.getMessageManager(Utils.CurrentBrowser);
mm.sendAsyncMessage('AccessFu:ShowCurrent', { move: aMove });
mm.sendAsyncMessage('AccessFu:AutoMove', aOptions);
},
announce: function announce(aAnnouncement) {

View File

@ -0,0 +1,269 @@
/* 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/. */
let Ci = Components.interfaces;
let Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
'resource://gre/modules/Services.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'TraversalRules',
'resource://gre/modules/accessibility/TraversalRules.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
'resource://gre/modules/accessibility/Presentation.jsm');
this.EXPORTED_SYMBOLS = ['ContentControl'];
this.ContentControl = function ContentControl(aContentScope) {
this._contentScope = Cu.getWeakReference(aContentScope);
this._vcCache = new WeakMap();
this._childMessageSenders = new WeakMap();
};
this.ContentControl.prototype = {
messagesOfInterest: ['AccessFu:MoveCursor',
'AccessFu:ClearCursor',
'AccessFu:MoveToPoint',
'AccessFu:AutoMove'],
start: function ContentControl_start() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.addMessageListener(message, this);
}
},
stop: function ContentControl_stop() {
let cs = this._contentScope.get();
for (let message of this.messagesOfInterest) {
cs.removeMessageListener(message, this);
}
},
get document() {
return this._contentScope.get().content.document;
},
get window() {
return this._contentScope.get().content;
},
get vc() {
return Utils.getVirtualCursor(this.document);
},
receiveMessage: function ContentControl_receiveMessage(aMessage) {
Logger.debug(() => {
return ['ContentControl.receiveMessage',
this.document.location.toString(),
JSON.stringify(aMessage.json)];
});
try {
switch (aMessage.name) {
case 'AccessFu:MoveCursor':
this.handleMove(aMessage);
break;
case 'AccessFu:ClearCursor':
this.handleClear(aMessage);
break;
case 'AccessFu:MoveToPoint':
this.handleMoveToPoint(aMessage);
break;
case 'AccessFu:AutoMove':
this.handleAutoMove(aMessage);
break;
default:
break;
}
} catch (x) {
Logger.logException(
x, 'Error handling message: ' + JSON.stringify(aMessage.json));
}
},
handleMove: function ContentControl_handleMove(aMessage) {
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let vc = this.vc;
if (origin != 'child' && this.sendToChild(vc, aMessage)) {
// Forwarded succesfully to child cursor.
return;
}
let moved = vc[action](TraversalRules[aMessage.json.rule]);
if (moved) {
if (origin === 'child') {
// We just stepped out of a child, clear child cursor.
Utils.getMessageManager(aMessage.target).sendAsyncMessage(
'AccessFu:ClearCursor', {});
} else {
// We potentially landed on a new child cursor. If so, we want to
// either be on the first or last item in the child doc.
let childAction = action;
if (action === 'moveNext') {
childAction = 'moveFirst';
} else if (action === 'movePrevious') {
childAction = 'moveLast';
}
// Attempt to forward move to a potential child cursor in our
// new position.
this.sendToChild(vc, aMessage, { action: childAction});
}
} else if (!this._childMessageSenders.has(aMessage.target)) {
// We failed to move, and the message is not from a child, so forward
// to parent.
this.sendToParent(aMessage);
}
},
handleMoveToPoint: function ContentControl_handleMoveToPoint(aMessage) {
let [x, y] = [aMessage.json.x, aMessage.json.y];
let rule = TraversalRules[aMessage.json.rule];
let vc = this.vc;
let win = this.window;
let dpr = win.devicePixelRatio;
this.vc.moveToPoint(rule, x * dpr, y * dpr, true);
let delta = Utils.isContentProcess ?
{ x: x - win.mozInnerScreenX, y: y - win.mozInnerScreenY } : {};
this.sendToChild(vc, aMessage, delta);
},
handleClear: function ContentControl_handleClear(aMessage) {
this.sendToChild(this.vc, aMessage);
this.vc.position = null;
},
handleAutoMove: function ContentControl_handleAutoMove(aMessage) {
this.autoMove(null, aMessage.json);
},
getChildCursor: function ContentControl_getChildCursor(aAccessible) {
let acc = aAccessible || this.vc.position;
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
let domNode = acc.DOMNode;
let mm = this._childMessageSenders.get(domNode, null);
if (!mm) {
mm = Utils.getMessageManager(domNode);
mm.addWeakMessageListener('AccessFu:MoveCursor', this);
this._childMessageSenders.set(domNode, mm);
}
return mm;
}
return null;
},
sendToChild: function ContentControl_sendToChild(aVirtualCursor,
aMessage,
aReplacer) {
let mm = this.getChildCursor(aVirtualCursor.position);
if (!mm) {
return false;
}
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'parent';
for (let attr in aReplacer) {
newJSON[attr] = aReplacer[attr];
}
mm.sendAsyncMessage(aMessage.name, newJSON);
return true;
},
sendToParent: function ContentControl_sendToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
newJSON.origin = 'child';
aMessage.target.sendAsyncMessage(aMessage.name, newJSON);
},
/**
* Move cursor and/or present its location.
* aOptions could have any of these fields:
* - delay: in ms, before actual move is performed. Another autoMove call
* would cancel it. Useful if we want to wait for a possible trailing
* focus move. Default 0.
* - noOpIfOnScreen: if accessible is alive and visible, don't do anything.
* - forcePresent: present cursor location, whether we move or don't.
* - moveToFocused: if there is a focused accessible move to that. This takes
* precedence over given anchor.
* - moveMethod: pivot move method to use, default is 'moveNext',
*/
autoMove: function ContentControl_autoMove(aAnchor, aOptions = {}) {
let win = this.window;
win.clearTimeout(this._autoMove);
let moveFunc = () => {
let vc = this.vc;
let acc = aAnchor;
let rule = aOptions.onScreenOnly ?
TraversalRules.SimpleOnScreen : TraversalRules.Simple;
let forcePresentFunc = () => {
if (aOptions.forcePresent) {
this._contentScope.get().sendAsyncMessage(
'AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE,
vc.startOffset, vc.endOffset));
}
};
if (aOptions.noOpIfOnScreen &&
Utils.isAliveAndVisible(vc.position, true)) {
forcePresentFunc();
return;
}
if (aOptions.moveToFocused) {
acc = Utils.AccRetrieval.getAccessibleFor(
this.document.activeElement) || acc;
}
let moved = false;
let moveMethod = aOptions.moveMethod || 'moveNext'; // default is moveNext
let moveFirstOrLast = moveMethod in ['moveFirst', 'moveLast'];
if (!moveFirstOrLast || acc) {
// We either need next/previous or there is an anchor we need to use.
moved = vc[moveFirstOrLast ? 'moveNext' : moveMethod](rule, acc, true);
}
if (moveFirstOrLast && !moved) {
// We move to first/last after no anchor move happened or succeeded.
moved = vc[moveMethod](rule);
}
let sentToChild = this.sendToChild(vc, {
name: 'AccessFu:AutoMove',
json: aOptions
});
if (!moved && !sentToChild) {
forcePresentFunc();
}
};
if (aOptions.delay) {
this._autoMove = win.setTimeout(moveFunc, aOptions.delay);
} else {
moveFunc();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIMessageListener
])
};

View File

@ -98,18 +98,11 @@ this.EventManager.prototype = {
case 'wheel':
{
let attempts = 0;
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
let intervalId = this.contentScope.content.setInterval(() => {
if (!Utils.isAliveAndVisible(vc.position, true)) {
this.contentScope.content.clearInterval(intervalId);
let delta = aEvent.deltaX || aEvent.deltaY;
this.contentScope.content.setTimeout(() => {
vc[delta > 0 ? 'moveNext' : 'movePrevious'](TraversalRules.SimpleOnScreen);
}, 100);
} else if (++attempts > 5) {
this.contentScope.content.clearInterval(intervalId);
}
}, 150);
let delta = aEvent.deltaX || aEvent.deltaY;
this.contentScope.contentControl.autoMove(
null,
{ moveMethod: delta > 0 ? 'moveNext' : 'movePrevious',
onScreenOnly: true, noOpIfOnScreen: true, delay: 500 });
break;
}
case 'scroll':
@ -162,11 +155,6 @@ this.EventManager.prototype = {
let reason = event.reason;
let oldAccessible = event.oldAccessible;
if (oldAccessible && oldAccessible.role == Roles.INTERNAL_FRAME) {
let mm = Utils.getMessageManager(oldAccessible.DOMNode);
mm.sendAsyncMessage('AccessFu:ClearCursor', {});
}
if (this.editState.editing) {
aEvent.accessibleDocument.takeFocus();
}
@ -195,8 +183,7 @@ this.EventManager.prototype = {
}
case Events.SCROLLING_START:
{
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
vc.moveNext(TraversalRules.Simple, aEvent.accessible, true);
this.contentScope.contentControl.autoMove(aEvent.accessible);
break;
}
case Events.TEXT_CARET_MOVED:
@ -253,18 +240,25 @@ this.EventManager.prototype = {
}
case Events.HIDE:
{
let evt = aEvent.QueryInterface(Ci.nsIAccessibleHideEvent);
let {liveRegion, isPolite} = this._handleLiveRegion(
aEvent.QueryInterface(Ci.nsIAccessibleHideEvent),
['removals', 'all']);
// Only handle hide if it is a relevant live region.
if (!liveRegion) {
break;
evt, ['removals', 'all']);
if (liveRegion) {
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
break;
}
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
} else {
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
if (vc.position &&
(Utils.getState(vc.position).contains(States.DEFUNCT) ||
Utils.isInSubtree(vc.position, aEvent.accessible))) {
this.contentScope.contentControl.autoMove(
evt.targetPrevSibling || evt.targetParent,
{ moveToFocused: true, delay: 500 });
}
}
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
if (aEvent.accessible.role === Roles.TEXT_LEAF) {
break;
}
this._queueLiveEvent(Events.HIDE, liveRegion, isPolite);
break;
}
case Events.TEXT_INSERTED:
@ -285,18 +279,14 @@ this.EventManager.prototype = {
let acc = aEvent.accessible;
let doc = aEvent.accessibleDocument;
if (acc.role != Roles.DOCUMENT && doc.role != Roles.CHROME_WINDOW) {
this.contentScope.content.clearTimeout(this._autoMove);
let vc = Utils.getVirtualCursor(this.contentScope.content.document);
vc.moveNext(TraversalRules.Simple, acc, true);
}
break;
this.contentScope.contentControl.autoMove(acc);
}
break;
}
case Events.DOCUMENT_LOAD_COMPLETE:
{
this._autoMove = this.contentScope.content.setTimeout(() => {
Utils.getVirtualCursor(this.contentScope.content.document)
.moveNext(TraversalRules.Simple, aEvent.accessible, true);
}, 500);
this.contentScope.contentControl.autoMove(
aEvent.accessible, { delay: 500 });
break;
}
}

View File

@ -247,6 +247,24 @@ this.Utils = {
return new Rect(objX.value, objY.value, objW.value, objH.value);
},
isInSubtree: function isInSubtree(aAccessible, aSubTreeRoot) {
let acc = aAccessible;
while (acc) {
if (acc == aSubTreeRoot) {
return true;
}
try {
acc = acc.parent;
} catch (x) {
Logger.debug('Failed to get parent:', x);
acc = null;
}
}
return false;
},
inHiddenSubtree: function inHiddenSubtree(aAccessible) {
for (let acc=aAccessible; acc; acc=acc.parent) {
let hidden = Utils.getAttributes(acc).hidden;
@ -565,8 +583,13 @@ PivotContext.prototype = {
_getAncestry: function _getAncestry(aAccessible) {
let ancestry = [];
let parent = aAccessible;
while (parent && (parent = parent.parent)) {
ancestry.push(parent);
try {
while (parent && (parent = parent.parent)) {
ancestry.push(parent);
}
} catch (e) {
// A defunct accessible will raise an exception geting parent.
Logger.debug('Failed to get parent:', x);
}
return ancestry.reverse();
},

View File

@ -20,118 +20,15 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Utils',
'resource://gre/modules/accessibility/Utils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'EventManager',
'resource://gre/modules/accessibility/EventManager.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'ContentControl',
'resource://gre/modules/accessibility/ContentControl.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Roles',
'resource://gre/modules/accessibility/Constants.jsm');
Logger.debug('content-script.js');
let eventManager = null;
function clearCursor(aMessage) {
try {
Utils.getVirtualCursor(content.document).position = null;
forwardToChild(aMessage);
} catch (x) {
Logger.logException(x);
}
}
function moveCursor(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let vc = Utils.getVirtualCursor(content.document);
let origin = aMessage.json.origin;
let action = aMessage.json.action;
let rule = TraversalRules[aMessage.json.rule];
function moveCursorInner() {
try {
if (origin == 'parent' &&
!Utils.isAliveAndVisible(vc.position)) {
// We have a bad position in this frame, move vc to last or first item.
if (action == 'moveNext') {
return vc.moveFirst(rule);
} else if (action == 'movePrevious') {
return vc.moveLast(rule);
}
}
return vc[action](rule);
} catch (x) {
if (action == 'moveNext' || action == 'movePrevious') {
// If we are trying to move next/prev put the vc on the focused item.
let acc = Utils.AccRetrieval.
getAccessibleFor(content.document.activeElement);
return vc.moveNext(rule, acc, true);
} else {
throw x;
}
}
return false;
}
try {
if (origin != 'child' &&
forwardToChild(aMessage, moveCursor, vc.position)) {
// We successfully forwarded the move to the child document.
return;
}
if (moveCursorInner()) {
// If we moved, try forwarding the message to the new position,
// it may be a frame with a vc of its own.
forwardToChild(aMessage, moveCursor, vc.position);
} else {
// If we did not move, we probably reached the end or start of the
// document, go back to parent content and move us out of the iframe.
if (origin == 'parent') {
vc.position = null;
}
forwardToParent(aMessage);
}
} catch (x) {
Logger.logException(x, 'Cursor move failed');
}
}
function moveToPoint(aMessage) {
if (Logger.logLevel >= Logger.DEBUG) {
Logger.debug(aMessage.name, JSON.stringify(aMessage.json, null, ' '));
}
let vc = Utils.getVirtualCursor(content.document);
let details = aMessage.json;
let rule = TraversalRules[details.rule];
try {
let dpr = content.devicePixelRatio;
vc.moveToPoint(rule, details.x * dpr, details.y * dpr, true);
forwardToChild(aMessage, moveToPoint, vc.position);
} catch (x) {
Logger.logException(x, 'Failed move to point');
}
}
function showCurrent(aMessage) {
Logger.debug(() => {
return [aMessage.name, JSON.stringify(aMessage.json, null, ' ')];
});
let vc = Utils.getVirtualCursor(content.document);
if (!forwardToChild(vc, showCurrent, aMessage)) {
if (!vc.position && aMessage.json.move) {
vc.moveFirst(TraversalRules.Simple);
} else {
sendAsyncMessage('AccessFu:Present', Presentation.pivotChanged(
vc.position, null, Ci.nsIAccessiblePivot.REASON_NONE,
vc.startOffset, vc.endOffset));
}
}
}
let contentControl = null;
function forwardToParent(aMessage) {
// XXX: This is a silly way to make a deep copy
@ -385,22 +282,23 @@ addMessageListener(
if (m.json.buildApp)
Utils.MozBuildApp = m.json.buildApp;
addMessageListener('AccessFu:MoveToPoint', moveToPoint);
addMessageListener('AccessFu:MoveCursor', moveCursor);
addMessageListener('AccessFu:ShowCurrent', showCurrent);
addMessageListener('AccessFu:Activate', activateCurrent);
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
addMessageListener('AccessFu:Scroll', scroll);
addMessageListener('AccessFu:AdjustRange', adjustRange);
addMessageListener('AccessFu:MoveCaret', moveCaret);
addMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
addMessageListener('AccessFu:ClearCursor', clearCursor);
if (!eventManager) {
eventManager = new EventManager(this);
}
eventManager.start();
if (!contentControl) {
contentControl = new ContentControl(this);
}
contentControl.start();
sendAsyncMessage('AccessFu:ContentStarted');
});
@ -409,17 +307,14 @@ addMessageListener(
function(m) {
Logger.debug('AccessFu:Stop');
removeMessageListener('AccessFu:MoveToPoint', moveToPoint);
removeMessageListener('AccessFu:MoveCursor', moveCursor);
removeMessageListener('AccessFu:ShowCurrent', showCurrent);
removeMessageListener('AccessFu:Activate', activateCurrent);
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
removeMessageListener('AccessFu:Scroll', scroll);
removeMessageListener('AccessFu:MoveCaret', moveCaret);
removeMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
removeMessageListener('AccessFu:ClearCursor', clearCursor);
eventManager.stop();
contentControl.stop();
});
sendAsyncMessage('AccessFu:Ready');

View File

@ -9,6 +9,7 @@ JS_MODULES_PATH = 'modules/accessibility'
EXTRA_JS_MODULES += [
'AccessFu.jsm',
'Constants.jsm',
'ContentControl.jsm',
'EventManager.jsm',
'OutputGenerator.jsm',
'Presentation.jsm',

View File

@ -12,18 +12,56 @@
'</body>' +
'</html>';
function showAlert() {
document.getElementById('alert').hidden = false;
}
function hideAlert() {
document.getElementById('alert').hidden = true;
}
</script>
<style>
#windows > iframe {
#windows {
position: relative;
width: 320px;
height: 480px;
}
#windows > iframe {
z-index: 1;
}
#windows > div[role='dialog'] {
z-index: 2;
background-color: pink;
}
#windows > * {
position: absolute;
width: 100%;
height: 100%;
}
iframe {
width: 100%;
height: 100%;
}
</style>
</head>
<body>
<div>Phone status bar</div>
<div id="windows"></div>
<button>Home</button>
<div id="windows">
<div id="appframe"></div>
<div role="dialog" id="alert" hidden>
<h1>This is an alert!</h1>
<p>Do you agree?</p>
<button onclick="hideAlert()">Yes</button>
<button onclick="hideAlert()">No</button>
</div>
</div>
<button id="home">Home</button>
</body>
</html>

View File

@ -216,13 +216,17 @@ AccessFuContentTest.prototype = {
if (this.currentPair) {
if (this.currentPair[0] instanceof Function) {
this.currentPair[0](this.mms[0]);
} else {
} else if (this.currentPair[0]) {
this.mms[0].sendAsyncMessage(this.currentPair[0].name,
this.currentPair[0].json);
this.currentPair[0].json);
}
if (!this.currentPair[1]) {
this.pump();
}
} else if (this.finishedCallback) {
for (var mm of this.mms) {
mm.sendAsyncMessage('AccessFu:Stop');
mm.sendAsyncMessage('AccessFu:Stop');
}
this.finishedCallback();
}

View File

@ -125,10 +125,10 @@
// Move cursor with focus in outside document
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[ focusFunc('button', false), { speak: 'Home button' }],
[ focusFunc('button#home', false), { speak: 'Home button' }],
// Blur button and reset cursor
[focusFunc('button', true), null],
[focusFunc('button#home', true), null],
[clearCursor, null],
// Set focus on element outside of embedded frame while cursor is in frame
@ -136,12 +136,53 @@
{ speak: 'Phone status bar Traversal Rule test document' }],
[simpleMoveNext,
{ speak: 'wow heading level 1 such app' }],
[focusFunc('button', false), { speak: 'Home button' }]
[focusFunc('button#home', false), { speak: 'Home button' }]
// Blur button and reset cursor
[focusFunc('button#home', true), null],
[clearCursor, null],
// XXX: Set focus on iframe itself.
// XXX: Set focus on element in iframe when cursor is outside of it.
// XXX: Set focus on element in iframe when cursor is in iframe.
]);
// Open dialog in outer doc, while cursor is also in outer doc
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
[function () { doc.defaultView.hideAlert() },
{ speak: 'wow heading level 1 such app' }],
[clearCursor, null],
// Open dialog in outer doc, while cursor is in inner frame
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[simpleMoveNext,
{ speak: 'wow heading level 1 such app' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
// XXX: Place cursor back where it was.
[doc.defaultView.hideAlert,
{ speak: 'many option not checked check button such app' }],
[clearCursor, null],
// Open dialog, then focus on something when closing
[simpleMoveNext,
{ speak: 'Phone status bar Traversal Rule test document' }],
[doc.defaultView.showAlert,
{ speak: 'This is an alert! heading level 1 dialog' }],
[function () {
doc.defaultView.hideAlert();
doc.querySelector('button#home').focus();
},
{ speak: 'Home button Traversal Rule test document' }]
]);
contentTest.start(function () {
closeBrowserWindow();
@ -150,7 +191,7 @@
});
iframe.src = 'data:text/html;charset=utf-8,' + doc.defaultView.frameContents;
doc.querySelector('#windows').appendChild(iframe);
doc.getElementById('appframe').appendChild(iframe);
}
SimpleTest.waitForExplicitFinish();

View File

@ -28,8 +28,6 @@ LIBS += \
endif
endif
STL_FLAGS=
LIBS += $(JEMALLOC_LIBS)
LIBS += \

View File

@ -41,3 +41,5 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
]
LDFLAGS += ['-Wl,--export-dynamic']
DISABLE_STL_WRAPPING = True

View File

@ -26,8 +26,6 @@ endif #} LIBXUL_SDK
# Build a binary bootstrapping with XRE_main
STL_FLAGS=
LIBS += \
$(XPCOM_STANDALONE_GLUE_LDOPTS) \
$(NULL)
@ -55,7 +53,6 @@ NSDISTMODE = copy
include $(topsrcdir)/config/config.mk
ifeq ($(OS_ARCH),WINNT)
RCINCLUDE = splash.rc
# Rebuild firefox.exe if the manifest changes - it's included by splash.rc.
# (this dependency should really be just for firefox.exe, not other targets)
# Note the manifest file exists in the tree, so we use the explicit filename

View File

@ -39,6 +39,7 @@ if CONFIG['_MSC_VER']:
WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
if CONFIG['OS_ARCH'] == 'WINNT':
RCINCLUDE = 'splash.rc'
DEFINES['MOZ_PHOENIX'] = True
# Control the default heap size.
@ -51,3 +52,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
# Set it to 256k. See bug 127069.
if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
LDFLAGS += ['/HEAP:0x40000']
DISABLE_STL_WRAPPING = True

View File

@ -291,8 +291,8 @@ PlacesController.prototype = {
* @param aIsMoveCommand
* True if the command for which this method is called only moves the
* selected items to another container, false otherwise.
* @returns true if all nodes in the selection can be removed,
* false otherwise.
* @return true if all nodes in the selection can be removed,
* false otherwise.
*/
_hasRemovableSelection: function PC__hasRemovableSelection(aIsMoveCommand) {
var ranges = this._view.removableSelectionRanges;
@ -356,9 +356,9 @@ PlacesController.prototype = {
* Looks at the data on the clipboard to see if it is paste-able.
* Paste-able data is:
* - in a format that the view can receive
* @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
- clipboard data is of type TEXT_UNICODE and
is a valid URI.
* @return true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor,
* - clipboard data is of type TEXT_UNICODE and
* is a valid URI.
*/
_isClipboardDataPasteable: function PC__isClipboardDataPasteable() {
// if the clipboard contains TYPE_X_MOZ_PLACE_* data, it is definitely
@ -413,10 +413,10 @@ PlacesController.prototype = {
* "separator" node is a separator line
* "host" node is a host
*
* @returns an array of objects corresponding the selected nodes. Each
* object has each of the properties above set if its corresponding
* node matches the rule. In addition, the annotations names for each
* node are set on its corresponding object as properties.
* @return an array of objects corresponding the selected nodes. Each
* object has each of the properties above set if its corresponding
* node matches the rule. In addition, the annotations names for each
* node are set on its corresponding object as properties.
* Notes:
* 1) This can be slow, so don't call it anywhere performance critical!
* 2) A single-object array corresponding the root node is returned if
@ -503,8 +503,8 @@ PlacesController.prototype = {
* the context menu item
* @param aMetaData
* meta data about the selection
* @returns true if the conditions (see buildContextMenu) are satisfied
* and the item can be displayed, false otherwise.
* @return true if the conditions (see buildContextMenu) are satisfied
* and the item can be displayed, false otherwise.
*/
_shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) {
var selectiontype = aMenuItem.getAttribute("selectiontype");
@ -782,7 +782,7 @@ PlacesController.prototype = {
* Node to check for containment.
* @param pastFolders
* List of folders the calling function has already traversed
* @returns true if the node should be skipped, false otherwise.
* @return true if the node should be skipped, false otherwise.
*/
_shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) {
/**
@ -791,7 +791,7 @@ PlacesController.prototype = {
* The node to check for containment for
* @param parent
* The parent container to check for containment in
* @returns true if node is a member of parent's children, false otherwise.
* @return true if node is a member of parent's children, false otherwise.
*/
function isContainedBy(node, parent) {
var cursor = node.parent;
@ -1008,7 +1008,6 @@ PlacesController.prototype = {
*/
setDataTransfer: function PC_setDataTransfer(aEvent) {
let dt = aEvent.dataTransfer;
let doCopy = ["copyLink", "copy", "link"].indexOf(dt.effectAllowed) != -1;
let result = this._view.result;
let didSuppressNotifications = result.suppressNotifications;
@ -1016,7 +1015,7 @@ PlacesController.prototype = {
result.suppressNotifications = true;
function addData(type, index, overrideURI) {
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI, doCopy);
let wrapNode = PlacesUtils.wrapNode(node, type, overrideURI);
dt.mozSetDataAt(type, wrapNode, index);
}
@ -1116,11 +1115,10 @@ PlacesController.prototype = {
let livemarkInfo = this.getCachedLivemarkInfo(node);
let overrideURI = livemarkInfo ? livemarkInfo.feedURI.spec : null;
let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node);
contents.forEach(function (content) {
content.entries.push(
PlacesUtils.wrapNode(node, content.type, overrideURI, resolveShortcuts)
PlacesUtils.wrapNode(node, content.type, overrideURI)
);
});
}, this);
@ -1262,6 +1260,13 @@ PlacesController.prototype = {
if (ip.index != PlacesUtils.bookmarks.DEFAULT_INDEX)
insertionIndex = ip.index + i;
// If this is not a copy, check for safety that we can move the source,
// otherwise report an error and fallback to a copy.
if (action != "copy" && !PlacesControllerDragHelper.canMoveUnwrappedNode(items[i])) {
Components.utils.reportError("Tried to move an unmovable Places node, " +
"reverting to a copy operation.");
action = "copy";
}
transactions.push(
PlacesUIUtils.makeTransaction(items[i], type, ip.itemId,
insertionIndex, action == "copy")
@ -1304,7 +1309,7 @@ PlacesController.prototype = {
* @param aNode
* a places result node.
* @return true if there's a cached mozILivemarkInfo object for
* aNode, false otherwise.
* aNode, false otherwise.
*/
hasCachedLivemarkInfo: function PC_hasCachedLivemarkInfo(aNode)
this._cachedLivemarkInfoObjects.has(aNode),
@ -1338,8 +1343,8 @@ let PlacesControllerDragHelper = {
* mouse is dragging over one of its submenus
* @param node
* The container node
* @returns true if the user is dragging over a node within the hierarchy of
* the container, false otherwise.
* @return true if the user is dragging over a node within the hierarchy of
* the container, false otherwise.
*/
draggingOverChildNode: function PCDH_draggingOverChildNode(node) {
let currentNode = this.currentDropTarget;
@ -1352,7 +1357,7 @@ let PlacesControllerDragHelper = {
},
/**
* @returns The current active drag session. Returns null if there is none.
* @return The current active drag session. Returns null if there is none.
*/
getSession: function PCDH__getSession() {
return this.dragService.getCurrentSession();
@ -1438,13 +1443,28 @@ let PlacesControllerDragHelper = {
return true;
},
/**
* Determines if an unwrapped node can be moved.
*
* @param aUnwrappedNode
* A node unwrapped by PlacesUtils.unwrapNodes().
* @return True if the node can be moved, false otherwise.
*/
canMoveUnwrappedNode: function (aUnwrappedNode) {
return aUnwrappedNode.id > 0 &&
!PlacesUtils.isRootItem(aUnwrappedNode.id) &&
aUnwrappedNode.parent != PlacesUtils.placesRootId &&
aUnwrappedNode.parent != PlacesUtils.tagsFolderId &&
aUnwrappedNode.grandParentId != PlacesUtils.tagsFolderId &&
!aUnwrappedNode.parentReadOnly;
},
/**
* Determines if a node can be moved.
*
* @param aNode
* A nsINavHistoryResultNode node.
* @returns True if the node can be moved, false otherwise.
* @return True if the node can be moved, false otherwise.
*/
canMoveNode:
function PCDH_canMoveNode(aNode) {
@ -1478,7 +1498,7 @@ let PlacesControllerDragHelper = {
* A bookmark folder id.
* @param [optional] aParentId
* The parent id of the folder.
* @returns True if the container can be moved to the target.
* @return True if the container can be moved to the target.
*/
canMoveContainer:
function PCDH_canMoveContainer(aId, aParentId) {
@ -1555,6 +1575,13 @@ let PlacesControllerDragHelper = {
transactions.push(tagTxn);
}
else {
// If this is not a copy, check for safety that we can move the source,
// otherwise report an error and fallback to a copy.
if (!doCopy && !PlacesControllerDragHelper.canMoveUnwrappedNode(unwrapped)) {
Components.utils.reportError("Tried to move an unmovable Places node, " +
"reverting to a copy operation.");
doCopy = true;
}
transactions.push(PlacesUIUtils.makeTransaction(unwrapped,
flavor, insertionPoint.itemId,
index, doCopy));

View File

@ -857,6 +857,8 @@ var gEditItemOverlay = {
var txn = new PlacesCreateFolderTransaction(defaultLabel, ip.itemId, ip.index);
PlacesUtils.transactionManager.doTransaction(txn);
this._folderTree.focus();
this._folderTree.selectItems([ip.itemId]);
PlacesUtils.asContainer(this._folderTree.selectedNode).containerOpen = true;
this._folderTree.selectItems([this._lastNewItem]);
this._folderTree.startEditing(this._folderTree.view.selection.currentIndex,
this._folderTree.columns.getFirstColumn());

View File

@ -62,7 +62,7 @@ var PlacesOrganizer = {
for (let container of hierarchy) {
switch (typeof container) {
case "number":
this._places.selectItems([container]);
this._places.selectItems([container], false);
break;
case "string":
if (container.substr(0, 6) == "place:")
@ -327,7 +327,7 @@ var PlacesOrganizer = {
openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
if (aContainer.itemId != -1)
this._places.selectItems([aContainer.itemId]);
this._places.selectItems([aContainer.itemId], false);
else if (PlacesUtils.nodeIsQuery(aContainer))
this._places.selectPlaceURI(aContainer.uri);
},

View File

@ -45,7 +45,7 @@
<property name="view">
<getter><![CDATA[
try {
return this.treeBoxObject.view.wrappedJSObject;
return this.treeBoxObject.view.wrappedJSObject || null;
}
catch(e) {
return null;
@ -312,9 +312,9 @@
for (let i = 0; i < rc; ++i) {
let min = { }, max = { };
selection.getRangeAt(i, min, max);
for (let j = min.value; j <= max.value; ++j)
for (let j = min.value; j <= max.value; ++j) {
nodes.push(resultview.nodeForTreeIndex(j));
}
}
return nodes;
]]></getter>
@ -453,8 +453,6 @@
//
// If the sole selection is the bookmarks toolbar folder, we insert
// into it even if it is not opened
var itemId =
PlacesUtils.getConcreteItemId(resultView.nodeForTreeIndex(max.value));
if (selection.count == 1 && resultView.isContainer(max.value) &&
!this.flatList)
orientation = Ci.nsITreeView.DROP_ON;
@ -546,13 +544,16 @@
</method>
<!-- This method will select the first node in the tree that matches
each given item id. It will open any parent nodes that it needs
each given item id. It will open any folder nodes that it needs
to in order to show the selected items.
-->
<method name="selectItems">
<parameter name="aIDs"/>
<parameter name="aOpenContainers"/>
<body><![CDATA[
// Never open containers in flat lists.
if (this.flatList)
aOpenContainers = false;
// By default, we do search and select within containers which were
// closed (note that containers in which nodes were not found are
// closed).
@ -601,8 +602,13 @@
nodesURIChecked.indexOf(node.uri) != -1)
return foundOne;
// Don't try to open a query or a shurtcut, since it may return
// any duplicate data and be infinitely nested. Though, if it has
// been explicitly opened by the caller, search into it.
let shouldOpen = aOpenContainers &&
node.type == Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER;
PlacesUtils.asContainer(node);
if (!aOpenContainers && !node.containerOpen)
if (!node.containerOpen && !shouldOpen)
return foundOne;
nodesURIChecked.push(node.uri);

View File

@ -452,7 +452,7 @@ PlacesTreeView.prototype = {
for (let i = 0; i < aNodesInfo.length; i++) {
let nodeInfo = aNodesInfo[i];
let row = this._getNewRowForRemovedNode(aUpdatedContainer,
aNodesInfo[i].node);
nodeInfo.node);
// Select the found node, if any.
if (row != -1) {
selection.rangedSelect(row, row, true);
@ -465,9 +465,11 @@ PlacesTreeView.prototype = {
// select the node at its old row, if any.
if (aNodesInfo.length == 1 && selection.count == 0) {
let row = Math.min(aNodesInfo[0].oldRow, this._rows.length - 1);
selection.rangedSelect(row, row, true);
if (aNodesInfo[0].wasVisible && scrollToRow == -1)
scrollToRow = aNodesInfo[0].oldRow;
if (row != -1) {
selection.rangedSelect(row, row, true);
if (aNodesInfo[0].wasVisible && scrollToRow == -1)
scrollToRow = aNodesInfo[0].oldRow;
}
}
if (scrollToRow != -1)
@ -705,7 +707,8 @@ PlacesTreeView.prototype = {
// Restore selection.
let rowToSelect = Math.min(oldRow, this._rows.length - 1);
this.selection.rangedSelect(rowToSelect, rowToSelect, true);
if (rowToSelect != -1)
this.selection.rangedSelect(rowToSelect, rowToSelect, true);
},
nodeMoved:

View File

@ -45,4 +45,5 @@ skip-if = true
[browser_416459_cut.js]
[browser_library_downloads.js]
[browser_library_left_pane_select_hierarchy.js]
[browser_435851_copy_query.js]
[browser_toolbarbutton_menu_context.js]

View File

@ -0,0 +1,59 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/* test that copying a non movable query or folder shortcut makes a new query with the same url, not a deep copy */
const SHORTCUT_URL = "place:folder=2";
const QUERY_URL = "place:sort=8&maxResults=10";
add_task(function copy_toolbar_shortcut() {
let library = yield promiseLibrary();
registerCleanupFunction(function () {
library.close();
PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
});
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
yield promiseClipboard(function () { library.PlacesOrganizer._places.controller.copy(); },
PlacesUtils.TYPE_X_MOZ_PLACE);
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
library.ContentTree.view.controller.paste();
let toolbarCopyNode = library.ContentTree.view.view.nodeForTreeIndex(0);
is(toolbarCopyNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
"copy is still a folder shortcut");
PlacesUtils.bookmarks.removeItem(toolbarCopyNode.itemId);
library.PlacesOrganizer.selectLeftPaneQuery("BookmarksToolbar");
is(library.PlacesOrganizer._places.selectedNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_FOLDER_SHORTCUT,
"original is still a folder shortcut");
});
add_task(function copy_history_query() {
let library = yield promiseLibrary();
library.PlacesOrganizer.selectLeftPaneQuery("History");
yield promiseClipboard(function () { library.PlacesOrganizer._places.controller.copy(); },
PlacesUtils.TYPE_X_MOZ_PLACE);
library.PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
library.ContentTree.view.controller.paste();
let historyCopyNode = library.ContentTree.view.view.nodeForTreeIndex(0);
is(historyCopyNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY,
"copy is still a query");
PlacesUtils.bookmarks.removeItem(historyCopyNode.itemId);
library.PlacesOrganizer.selectLeftPaneQuery("History");
is(library.PlacesOrganizer._places.selectedNode.type,
Ci.nsINavHistoryResultNode.RESULT_TYPE_QUERY,
"original is still a query");
});

View File

@ -173,6 +173,8 @@ gTests.push({
},
selectNode: function(tree) {
tree.selectItems([PlacesUtils.unfiledBookmarksFolderId]);
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
tree.selectItems([this._itemId]);
is(tree.selectedNode.itemId, this._itemId, "Bookmark has been selected");
},
@ -329,6 +331,8 @@ gTests.push({
},
selectNode: function(tree) {
tree.selectItems([PlacesUtils.unfiledBookmarksFolderId]);
PlacesUtils.asContainer(tree.selectedNode).containerOpen = true;
tree.selectItems([this._itemId]);
is(tree.selectedNode.itemId, this._itemId, "Bookmark has been selected");
},

View File

@ -29,6 +29,46 @@ function openLibrary(callback, aLeftPaneRoot) {
return library;
}
/**
* Returns a handle to a Library window.
* If one is opens returns itm otherwise it opens a new one.
*
* @param aLeftPaneRoot
* Hierarchy to open and select in the left pane.
*/
function promiseLibrary(aLeftPaneRoot) {
let deferred = Promise.defer();
let library = Services.wm.getMostRecentWindow("Places:Organizer");
if (library) {
if (aLeftPaneRoot)
library.PlacesOrganizer.selectLeftPaneContainerByHierarchy(aLeftPaneRoot);
deferred.resolve(library);
}
else {
openLibrary(aLibrary => deferred.resolve(aLibrary), aLeftPaneRoot);
}
return deferred.promise;
}
/**
* Waits for a clipboard operation to complete, looking for the expected type.
*
* @see waitForClipboard
*
* @param aPopulateClipboardFn
* Function to populate the clipboard.
* @param aFlavor
* Data flavor to expect.
*/
function promiseClipboard(aPopulateClipboardFn, aFlavor) {
let deferred = Promise.defer();
waitForClipboard(function (aData) !!aData,
aPopulateClipboardFn,
function () { deferred.resolve(); },
aFlavor);
return deferred.promise;
}
/**
* Waits for completion of a clear history operation, before
* proceeding with aCallback.

View File

@ -9,4 +9,5 @@ support-files = head.js
[test_bug549491.xul]
[test_bug631374_tags_selector_scroll.xul]
[test_editBookmarkOverlay_tags_liveUpdate.xul]
[test_selectItems_on_nested_tree.xul]
[test_treeview_date.xul]

View File

@ -0,0 +1,66 @@
<?xml version="1.0"?>
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="549192: History view not updated after deleting entry"
onload="runTest();">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript" src="head.js" />
<body xmlns="http://www.w3.org/1999/xhtml" />
<tree id="tree"
type="places"
flex="1">
<treecols>
<treecol label="Title" id="title" anonid="title" primary="true" ordinal="1" flex="1"/>
</treecols>
<treechildren flex="1"/>
</tree>
<script type="application/javascript"><![CDATA[
/**
* Ensure that selectItems doesn't recurse infinitely in nested trees.
*/
function runTest() {
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
NetUtil.newURI("place:folder=UNFILED_BOOKMARKS"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"shortcut");
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
NetUtil.newURI("place:folder=UNFILED_BOOKMARKS&maxResults=10"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"query");
let folderId = PlacesUtils.bookmarks.createFolder(PlacesUtils.unfiledBookmarksFolderId,
"folder",
PlacesUtils.bookmarks.DEFAULT_INDEX);
let itemId = PlacesUtils.bookmarks.insertBookmark(folderId,
NetUtil.newURI("http://www.mozilla.org/"),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bookmark");
// Setup the places tree contents.
var tree = document.getElementById("tree");
tree.place = "place:folder=UNFILED_BOOKMARKS";
// Select the last bookmark.
tree.selectItems([itemId]);
is (tree.selectedNode.itemId, itemId, "The right node was selected");
}
]]></script>
</window>

View File

@ -5,7 +5,6 @@
include $(topsrcdir)/config/config.mk
DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)
RCINCLUDE = CommandExecuteHandler.rc
# Don't link against mozglue.dll
MOZ_GLUE_LDFLAGS =

View File

@ -18,3 +18,5 @@ for var in ('UNICODE', '_UNICODE', 'NS_NO_XPCOM'):
DEFINES[var] = True
NO_PGO = True
RCINCLUDE = 'CommandExecuteHandler.rc'

View File

@ -3,7 +3,6 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.
MODULES = stlport
STL_FLAGS =
# Force to build a static library, instead of a fake library, without
# installing it in dist/lib.

View File

@ -52,3 +52,4 @@ LOCAL_INCLUDES += [
'stlport',
]
DISABLE_STL_WRAPPING = True

View File

@ -2,7 +2,6 @@
# 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/.
STL_FLAGS =
NO_EXPAND_LIBS = 1
include $(topsrcdir)/config/rules.mk

View File

@ -17,3 +17,5 @@ if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
FORCE_STATIC_LIB = True
NO_PGO = True
DISABLE_STL_WRAPPING = True

View File

@ -590,7 +590,7 @@ endif
endif
COMPILE_CFLAGS = $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CFLAGS) $(CFLAGS) $(MOZBUILD_CFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CXXFLAGS = $(STL_FLAGS) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CXXFLAGS = $(if $(DISABLE_STL_WRAPPING),,$(STL_FLAGS)) $(VISIBILITY_FLAGS) $(DEFINES) $(INCLUDES) $(DSO_CFLAGS) $(DSO_PIC_CFLAGS) $(RTL_FLAGS) $(OS_CPPFLAGS) $(OS_COMPILE_CXXFLAGS) $(CXXFLAGS) $(MOZBUILD_CXXFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CMFLAGS = $(OS_COMPILE_CMFLAGS) $(MOZBUILD_CMFLAGS) $(EXTRA_COMPILE_FLAGS)
COMPILE_CMMFLAGS = $(OS_COMPILE_CMMFLAGS) $(MOZBUILD_CMMFLAGS) $(EXTRA_COMPILE_FLAGS)
ASFLAGS += $(EXTRA_ASSEMBLER_FLAGS)
@ -900,7 +900,7 @@ DEFINES += -DUNICODE -D_UNICODE
LOCAL_INCLUDES += -I'$(MOZ_DIRECTX_SDK_PATH)/include'
endif
STL_FLAGS=
DISABLE_STL_WRAPPING := 1
# Skip most Mozilla-specific include locations.
INCLUDES = -I. $(LOCAL_INCLUDES) -I$(DEPTH)/dist/include
endif

View File

@ -1088,7 +1088,8 @@ protected:
* Add/remove this element to the documents id cache
*/
void AddToIdTable(nsIAtom* aId);
void RemoveFromIdTable();
void RemoveFromIdTable(); // checks HasID() and uses DoGetID()
void RemoveFromIdTable(nsIAtom* aId);
/**
* Functions to carry out event default actions for links of all types

View File

@ -19,6 +19,7 @@
#include "nsPIDOMWindow.h" // for use in inline functions
#include "nsPropertyTable.h" // for member
#include "nsTHashtable.h" // for member
#include "nsWeakReference.h"
#include "mozilla/dom/DocumentBinding.h"
#include "mozilla/WeakPtr.h"
#include "Units.h"
@ -79,7 +80,6 @@ class nsWindowSizes;
class nsSmallVoidArray;
class nsDOMCaretPosition;
class nsViewportInfo;
class nsDOMEvent;
class nsIGlobalObject;
class nsCSSSelectorList;
@ -102,6 +102,7 @@ class DOMImplementation;
class DOMStringList;
class Element;
struct ElementRegistrationOptions;
class Event;
class EventTarget;
class FrameRequestCallback;
class HTMLBodyElement;
@ -2003,8 +2004,8 @@ public:
enum ElementCallbackType {
eCreated,
eEnteredView,
eLeftView,
eAttached,
eDetached,
eAttributeChanged
};
@ -2065,8 +2066,8 @@ public:
already_AddRefed<nsINode>
ImportNode(nsINode& aNode, bool aDeep, mozilla::ErrorResult& rv) const;
nsINode* AdoptNode(nsINode& aNode, mozilla::ErrorResult& rv);
already_AddRefed<nsDOMEvent> CreateEvent(const nsAString& aEventType,
mozilla::ErrorResult& rv) const;
already_AddRefed<mozilla::dom::Event>
CreateEvent(const nsAString& aEventType, mozilla::ErrorResult& rv) const;
already_AddRefed<nsRange> CreateRange(mozilla::ErrorResult& rv);
already_AddRefed<mozilla::dom::NodeIterator>
CreateNodeIterator(nsINode& aRoot, uint32_t aWhatToShow,

View File

@ -15,7 +15,6 @@
#include "nsNodeInfoManager.h" // for use in NodePrincipal()
#include "nsPropertyTable.h" // for typedefs
#include "nsTObserverArray.h" // for member
#include "nsWindowMemoryReporter.h" // for NS_DECL_SIZEOF_EXCLUDING_THIS
#include "mozilla/ErrorResult.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/dom/EventTarget.h" // for base class
@ -244,13 +243,21 @@ private:
// ever passed to Mutated().
enum { eMaxMutations = 300 };
// sMutationCount is a global mutation counter which is decreased by one at
// every mutation. It is capped at 0 to avoid wrapping.
// Its value is always between 0 and 300, inclusive.
static uint32_t sMutationCount;
};
// This should be used for any nsINode sub-class that has fields of its own
// that it needs to measure; any sub-class that doesn't use it will inherit
// SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be
// defined, it is inherited from nsINode.
// This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT.
#define NS_DECL_SIZEOF_EXCLUDING_THIS \
virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
// Categories of node properties
// 0 is global.
#define DOM_USER_DATA 1

View File

@ -719,23 +719,29 @@ void
Element::RemoveFromIdTable()
{
if (HasID()) {
if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
ShadowRoot* containingShadow = GetContainingShadow();
// Check for containingShadow because it may have
// been deleted during unlinking.
if (containingShadow) {
containingShadow->RemoveFromIdTable(this, DoGetID());
}
} else {
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsIAtom* id = DoGetID();
// id can be null during mutation events evilness. Also, XUL elements
// loose their proto attributes during cc-unlink, so this can happen
// during cc-unlink too.
if (id) {
doc->RemoveFromIdTable(this, DoGetID());
}
RemoveFromIdTable(DoGetID());
}
}
void
Element::RemoveFromIdTable(nsIAtom* aId)
{
NS_ASSERTION(HasID(), "Node doesn't have an ID?");
if (HasFlag(NODE_IS_IN_SHADOW_TREE)) {
ShadowRoot* containingShadow = GetContainingShadow();
// Check for containingShadow because it may have
// been deleted during unlinking.
if (containingShadow) {
containingShadow->RemoveFromIdTable(this, aId);
}
} else {
nsIDocument* doc = GetCurrentDoc();
if (doc && (!IsInAnonymousSubtree() || doc->IsXUL())) {
// id can be null during mutation events evilness. Also, XUL elements
// loose their proto attributes during cc-unlink, so this can happen
// during cc-unlink too.
if (aId) {
doc->RemoveFromIdTable(this, aId);
}
}
}
@ -1158,8 +1164,8 @@ Element::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
SetInDocument();
if (GetCustomElementData()) {
// Enqueue an enteredView callback for the custom element.
aDocument->EnqueueLifecycleCallback(nsIDocument::eEnteredView, this);
// Enqueue an attached callback for the custom element.
aDocument->EnqueueLifecycleCallback(nsIDocument::eAttached, this);
}
// Unset this flag since we now really are in a document.
@ -1321,8 +1327,8 @@ Element::UnbindFromTree(bool aDeep, bool aNullParent)
document->ClearBoxObjectFor(this);
if (GetCustomElementData()) {
// Enqueue a leftView callback for the custom element.
document->EnqueueLifecycleCallback(nsIDocument::eLeftView, this);
// Enqueue a detached callback for the custom element.
document->EnqueueLifecycleCallback(nsIDocument::eDetached, this);
}
}

View File

@ -193,6 +193,7 @@
#include "nsIAppsService.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/DocumentFragment.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/HTMLBodyElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/NodeFilterBinding.h"
@ -203,7 +204,6 @@
#include "nsDOMCaretPosition.h"
#include "nsIDOMHTMLTextAreaElement.h"
#include "nsViewportInfo.h"
#include "nsDOMEvent.h"
#include "nsIContentPermissionPrompt.h"
#include "mozilla/StaticPtr.h"
#include "nsITextControlElement.h"
@ -221,6 +221,7 @@
#include "nsIMutableArray.h"
#include "nsContentPermissionHelper.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsWindowMemoryReporter.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -330,11 +331,11 @@ CustomElementCallback::Call()
static_cast<LifecycleCreatedCallback *>(mCallback.get())->Call(mThisObject, rv);
mOwnerData->mElementIsBeingCreated = false;
break;
case nsIDocument::eEnteredView:
static_cast<LifecycleEnteredViewCallback *>(mCallback.get())->Call(mThisObject, rv);
case nsIDocument::eAttached:
static_cast<LifecycleAttachedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
case nsIDocument::eLeftView:
static_cast<LifecycleLeftViewCallback *>(mCallback.get())->Call(mThisObject, rv);
case nsIDocument::eDetached:
static_cast<LifecycleDetachedCallback *>(mCallback.get())->Call(mThisObject, rv);
break;
case nsIDocument::eAttributeChanged:
static_cast<LifecycleAttributeChangedCallback *>(mCallback.get())->Call(mThisObject,
@ -1749,16 +1750,16 @@ CustomDefinitionsTraverse(CustomElementHashKey* aKey,
cb->NoteXPCOMChild(aDefinition->mCallbacks->mCreatedCallback.Value());
}
if (callbacks->mEnteredViewCallback.WasPassed()) {
if (callbacks->mAttachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
"mCustomDefinitions->mCallbacks->mEnteredViewCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mEnteredViewCallback.Value());
"mCustomDefinitions->mCallbacks->mAttachedCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mAttachedCallback.Value());
}
if (callbacks->mLeftViewCallback.WasPassed()) {
if (callbacks->mDetachedCallback.WasPassed()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb,
"mCustomDefinitions->mCallbacks->mLeftViewCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mLeftViewCallback.Value());
"mCustomDefinitions->mCallbacks->mDetachedCallback");
cb->NoteXPCOMChild(aDefinition->mCallbacks->mDetachedCallback.Value());
}
return PL_DHASH_NEXT;
@ -5570,15 +5571,15 @@ nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
}
break;
case nsIDocument::eEnteredView:
if (definition->mCallbacks->mEnteredViewCallback.WasPassed()) {
func = definition->mCallbacks->mEnteredViewCallback.Value();
case nsIDocument::eAttached:
if (definition->mCallbacks->mAttachedCallback.WasPassed()) {
func = definition->mCallbacks->mAttachedCallback.Value();
}
break;
case nsIDocument::eLeftView:
if (definition->mCallbacks->mLeftViewCallback.WasPassed()) {
func = definition->mCallbacks->mLeftViewCallback.Value();
case nsIDocument::eDetached:
if (definition->mCallbacks->mDetachedCallback.WasPassed()) {
func = definition->mCallbacks->mDetachedCallback.Value();
}
break;
@ -5921,12 +5922,12 @@ nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
EnqueueLifecycleCallback(nsIDocument::eCreated, elem, nullptr, definition);
if (elem->GetCurrentDoc()) {
// Normally callbacks can not be enqueued until the created
// callback has been invoked, however, the entered view callback
// callback has been invoked, however, the attached callback
// in element upgrade is an exception so pretend the created
// callback has been invoked.
elem->GetCustomElementData()->mCreatedCallbackInvoked = true;
EnqueueLifecycleCallback(nsIDocument::eEnteredView, elem, nullptr, definition);
EnqueueLifecycleCallback(nsIDocument::eAttached, elem, nullptr, definition);
}
}
}
@ -7648,7 +7649,7 @@ nsDocument::CreateEvent(const nsAString& aEventType, nsIDOMEvent** aReturn)
return rv.ErrorCode();
}
already_AddRefed<nsDOMEvent>
already_AddRefed<Event>
nsIDocument::CreateEvent(const nsAString& aEventType, ErrorResult& rv) const
{
nsIPresShell *shell = GetShell();

View File

@ -313,7 +313,7 @@ private:
// The this value to use for invocation of the callback.
nsRefPtr<mozilla::dom::Element> mThisObject;
nsRefPtr<mozilla::dom::CallbackFunction> mCallback;
// The type of callback (eCreated, eEnteredView, etc.)
// The type of callback (eCreated, eAttached, etc.)
nsIDocument::ElementCallbackType mType;
// Arguments to be passed to the callback,
// used by the attribute changed callback.

View File

@ -23,6 +23,7 @@
#include "nsIXULRuntime.h"
#include "nsIScriptError.h"
#include "nsIConsoleService.h"
#include "nsIMemoryReporter.h"
#include "nsIProtocolHandler.h"
#include "nsIScriptSecurityManager.h"
#include "nsIJSRuntimeService.h"

View File

@ -18,6 +18,9 @@
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsAsyncDOMEvent.h"
#include "nsAttrValueOrString.h"
#include "nsBindingManager.h"
@ -41,7 +44,6 @@
#include "nsFocusManager.h"
#include "nsFrameManager.h"
#include "nsFrameSelection.h"
#include "mozilla/dom/Element.h"
#include "nsGenericHTMLElement.h"
#include "nsGkAtoms.h"
#include "nsIAnonymousContentCreator.h"
@ -96,11 +98,9 @@
#include "nsCSSParser.h"
#include "HTMLLegendElement.h"
#include "nsWrapperCacheInlines.h"
#include "mozilla/dom/ShadowRoot.h"
#include "WrapperFactory.h"
#include "DocumentType.h"
#include <algorithm>
#include "nsDOMEvent.h"
#include "nsGlobalWindow.h"
#include "nsDOMMutationObserver.h"
@ -2633,7 +2633,7 @@ nsINode::GetAttributes()
}
bool
EventTarget::DispatchEvent(nsDOMEvent& aEvent,
EventTarget::DispatchEvent(Event& aEvent,
ErrorResult& aRv)
{
bool result = false;

View File

@ -69,18 +69,18 @@
#include "nsIContentSecurityPolicy.h"
#include "nsIChannelPolicy.h"
#include "nsChannelPolicy.h"
#include "mozilla/dom/Element.h"
#include "GeckoProfiler.h"
#include "nsObjectFrame.h"
#include "nsDOMClassInfo.h"
#include "nsWrapperCacheInlines.h"
#include "nsDOMJSUtils.h"
#include "nsDOMEvent.h"
#include "nsWidgetsCID.h"
#include "nsContentCID.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/Telemetry.h"
#ifdef XP_WIN
@ -307,7 +307,7 @@ nsPluginCrashedEvent::Run()
}
ErrorResult rv;
nsRefPtr<nsDOMEvent> event =
nsRefPtr<Event> event =
doc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"), rv);
nsCOMPtr<nsIDOMDataContainerEvent> containerEvent(do_QueryObject(event));
if (!containerEvent) {

View File

@ -2140,7 +2140,15 @@ HTMLInputElement::GetValueIfStepped(int32_t aStep,
return NS_OK;
}
if (GetValidityState(VALIDITY_STATE_STEP_MISMATCH) &&
// If the current value isn't aligned on a step, then shift the value to the
// nearest step that will cause the addition of aStep steps (further below)
// to |value| to hit the required value.
// (Instead of using GetValidityState(VALIDITY_STATE_STEP_MISMATCH) we have
// to check HasStepMismatch and pass true as its aUseZeroIfValueNaN argument
// since we need to treat the value "" as zero for stepping purposes even
// though we don't suffer from a step mismatch when our value is the empty
// string.)
if (HasStepMismatch(true) &&
value != minimum && value != maximum) {
if (aStep > 0) {
value -= NS_floorModulo(value - GetStepBase(), step);
@ -2778,7 +2786,7 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
if (!mParserCreating) {
SanitizeValue(value);
}
// else SanitizeValue will be called by DoneCreatingElement
// else DoneCreatingElement calls us again once mParserCreating is false
if (aSetValueChanged) {
SetValueChanged(true);
@ -2803,7 +2811,10 @@ HTMLInputElement::SetValueInternal(const nsAString& aValue,
numberControlFrame->SetValueOfAnonTextControl(value);
}
}
OnValueChanged(!mParserCreating);
if (!mParserCreating) {
OnValueChanged(true);
}
// else DoneCreatingElement calls us again once mParserCreating is false
}
if (mType == NS_FORM_INPUT_COLOR) {
@ -3641,12 +3652,20 @@ HTMLInputElement::StepNumberControlForUserEvent(int32_t aDirection)
// want to wipe out what they typed if they try to increment/decrement the
// value. Better is to highlight the value as being invalid so that they
// can correct what they typed.
// We pass 'true' for UpdateValidityUIBits' aIsFocused argument regardless
// because we need the UI to update _now_ or the user will wonder why the
// step behavior isn't functioning.
UpdateValidityUIBits(true);
UpdateState(true);
return;
// We only do this if there actually is a value typed in by/displayed to
// the user. (IsValid() can return false if the 'required' attribute is
// set and the value is the empty string.)
nsNumberControlFrame* numberControlFrame =
do_QueryFrame(GetPrimaryFrame());
if (numberControlFrame &&
!numberControlFrame->AnonTextControlIsEmpty()) {
// We pass 'true' for UpdateValidityUIBits' aIsFocused argument
// regardless because we need the UI to update _now_ or the user will
// wonder why the step behavior isn't functioning.
UpdateValidityUIBits(true);
UpdateState(true);
return;
}
}
Decimal newValue = Decimal::nan(); // unchanged if value will not change
@ -6433,7 +6452,7 @@ HTMLInputElement::IsRangeUnderflow() const
}
bool
HTMLInputElement::HasStepMismatch() const
HTMLInputElement::HasStepMismatch(bool aUseZeroIfValueNaN) const
{
if (!DoesStepApply()) {
return false;
@ -6441,8 +6460,12 @@ HTMLInputElement::HasStepMismatch() const
Decimal value = GetValueAsDecimal();
if (value.isNaN()) {
// The element can't suffer from step mismatch if it's value isn't a number.
return false;
if (aUseZeroIfValueNaN) {
value = 0;
} else {
// The element can't suffer from step mismatch if it's value isn't a number.
return false;
}
}
Decimal step = GetStep();

View File

@ -256,7 +256,7 @@ public:
bool HasPatternMismatch() const;
bool IsRangeOverflow() const;
bool IsRangeUnderflow() const;
bool HasStepMismatch() const;
bool HasStepMismatch(bool aUseZeroIfValueNaN = false) const;
bool HasBadInput() const;
void UpdateTooLongValidityState();
void UpdateValueMissingValidityState();

View File

@ -6,8 +6,8 @@
#include "mozilla/dom/UndoManager.h"
#include "mozilla/dom/DOMTransactionBinding.h"
#include "mozilla/dom/Event.h"
#include "nsDOMClassInfoID.h"
#include "nsDOMEvent.h"
#include "nsIClassInfo.h"
#include "nsIDOMDocument.h"
#include "nsIXPCScriptable.h"
@ -1142,7 +1142,7 @@ UndoManager::DispatchTransactionEvent(JSContext* aCx, const nsAString& aType,
return;
}
nsRefPtr<nsDOMEvent> event = mHostNode->OwnerDoc()->CreateEvent(
nsRefPtr<Event> event = mHostNode->OwnerDoc()->CreateEvent(
NS_LITERAL_STRING("domtransaction"), aRv);
if (aRv.Failed()) {
return;

View File

@ -71,6 +71,18 @@ function getStepBase(element) {
Number(element.getAttribute("value") || "NaN") || 0;
}
function hasStepMismatch(element) {
var value = element.value;
if (value == "") {
value = 0;
}
var step = getStep(element);
if (step == "any") {
return false;
}
return ((value - getStepBase(element)) % step) != 0;
}
function floorModulo(x, y) {
return (x - y * Math.floor(x / y));
}
@ -101,7 +113,7 @@ function expectedValueAfterStepUpOrDown(stepFactor, element) {
return value;
}
if (element.validity.stepMismatch &&
if (hasStepMismatch(element) &&
value != minimum && value != maximum) {
if (stepFactor > 0) {
value -= floorModulo(value - getStepBase(element), step);
@ -138,8 +150,8 @@ function test() {
var elem = document.getElementById("input");
elem.focus();
elem.min = -3;
elem.max = 3;
elem.min = -5;
elem.max = 5;
elem.step = 2;
var defaultValue = 0;
var oldVal, expectedVal;
@ -203,6 +215,22 @@ function test() {
sendString("abc");
synthesizeKey(key, {});
is(elem.value, "", "Test " + key + " does nothing when the input is invalid");
// Test that no value does not block UI initiated stepping:
oldVal = elem.value = "";
elem.setAttribute("required", "required");
elem.select();
expectedVal = expectedValAfterKeyEvent(key, elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test " + key + " for number control with value set to the empty string and with the 'required' attribute set");
// Same again:
expectedVal = expectedValAfterKeyEvent(key, elem);
synthesizeKey(key, {});
is(elem.value, expectedVal, "Test repeat of " + key + " for number control");
// Reset 'required' attribute:
elem.removeAttribute("required");
}
}

View File

@ -20,7 +20,6 @@
#include "nsICommandManager.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "nsDOMEvent.h"
class nsIEditor;
class nsIParser;

View File

@ -255,20 +255,29 @@ public:
* *aFinished is set to false by the caller. If the callee sets it to true,
* we'll finish the stream and not call this again.
*/
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
MOZ_ASSERT(mInputCount <= 1 && mOutputCount <= 1);
*aOutput = aInput;
}
/**
* Produce the next block of audio samples, before input is provided.
* ProcessBlock() will be called later, and it then should not change
* aOutput. This is used only for DelayNodeEngine in a feedback loop.
*/
virtual void ProduceBlockBeforeInput(AudioChunk* aOutput)
{
NS_NOTREACHED("ProduceBlockBeforeInput called on wrong engine\n");
}
/**
* Produce the next block of audio samples, given input samples in the aInput
* array. There is one input sample per active port in aInput, in order.
* This is the multi-input/output version of ProduceAudioBlock. Only one kind
* of ProduceAudioBlock is called on each node, depending on whether the
* This is the multi-input/output version of ProcessBlock. Only one kind
* of ProcessBlock is called on each node, depending on whether the
* number of inputs and outputs are both 1 or not.
*
* aInput is always guaranteed to not contain more input AudioChunks than the
@ -279,10 +288,10 @@ public:
* corresponding AudioNode, in which case it will be interpreted as a channel
* of silence.
*/
virtual void ProduceAudioBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished)
virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished)
{
MOZ_ASSERT(mInputCount > 1 || mOutputCount > 1);
// Only produce one output port, and drop all other input ports.

View File

@ -324,8 +324,8 @@ ConvertSegmentToAudioBlock(AudioSegment* aSegment, AudioChunk* aBlock)
}
void
AudioNodeExternalInputStream::ProduceOutput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags)
AudioNodeExternalInputStream::ProcessInput(GraphTime aFrom, GraphTime aTo,
uint32_t aFlags)
{
// According to spec, number of outputs is always 1.
mLastChunks.SetLength(1);

View File

@ -25,7 +25,7 @@ public:
AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
~AudioNodeExternalInputStream();
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
private:
// For storing pointers and data about input tracks, like the last TrackTick which

View File

@ -399,7 +399,7 @@ AudioNodeStream::UpMixDownMixChunk(const AudioChunk* aChunk,
// The MediaStreamGraph guarantees that this is actually one block, for
// AudioNodeStreams.
void
AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
AudioNodeStream::ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
{
EnsureTrack(AUDIO_TRACK, mSampleRate);
// No more tracks will be coming
@ -426,17 +426,10 @@ AudioNodeStream::ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags)
ObtainInputBlock(inputChunks[i], i);
}
bool finished = false;
#ifdef DEBUG
for (uint16_t i = 0; i < outputCount; ++i) {
// Alter mDuration so we can detect if ProduceAudioBlock fails to set
// chunks.
mLastChunks[i].mDuration--;
}
#endif
if (maxInputs <= 1 && mEngine->OutputCount() <= 1) {
mEngine->ProduceAudioBlock(this, inputChunks[0], &mLastChunks[0], &finished);
mEngine->ProcessBlock(this, inputChunks[0], &mLastChunks[0], &finished);
} else {
mEngine->ProduceAudioBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
mEngine->ProcessBlocksOnPorts(this, inputChunks, mLastChunks, &finished);
}
for (uint16_t i = 0; i < outputCount; ++i) {
NS_ASSERTION(mLastChunks[i].GetDuration() == WEBAUDIO_BLOCK_SIZE,

View File

@ -33,6 +33,9 @@ class AudioNodeEngine;
* integrates audio processing with the MediaStreamGraph.
*/
class AudioNodeStream : public ProcessedMediaStream {
typedef dom::ChannelCountMode ChannelCountMode;
typedef dom::ChannelInterpretation ChannelInterpretation;
public:
typedef mozilla::dom::AudioContext AudioContext;
@ -56,8 +59,8 @@ public:
mMuted(false)
{
MOZ_ASSERT(NS_IsMainThread());
mChannelCountMode = dom::ChannelCountMode::Max;
mChannelInterpretation = dom::ChannelInterpretation::Speakers;
mChannelCountMode = ChannelCountMode::Max;
mChannelInterpretation = ChannelInterpretation::Speakers;
// AudioNodes are always producing data
mHasCurrentData = true;
MOZ_COUNT_CTOR(AudioNodeStream);
@ -79,8 +82,13 @@ public:
// This consumes the contents of aData. aData will be emptied after this returns.
void SetRawArrayData(nsTArray<float>& aData);
void SetChannelMixingParameters(uint32_t aNumberOfChannels,
dom::ChannelCountMode aChannelCountMoe,
dom::ChannelInterpretation aChannelInterpretation);
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
ChannelInterpretation GetChannelInterpretation()
{
return mChannelInterpretation;
}
void SetAudioParamHelperStream()
{
MOZ_ASSERT(!mAudioParamStream, "Can only do this once");
@ -93,9 +101,9 @@ public:
void SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
double aStreamTime);
void SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
dom::ChannelCountMode aChannelCountMoe,
dom::ChannelInterpretation aChannelInterpretation);
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
ChannelCountMode aChannelCountMoe,
ChannelInterpretation aChannelInterpretation);
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE;
TrackTicks GetCurrentPosition();
bool IsAudioParamStream() const
{
@ -171,8 +179,8 @@ protected:
// The number of input channels that this stream requires. 0 means don't care.
uint32_t mNumberOfInputChannels;
// The mixing modes
dom::ChannelCountMode mChannelCountMode;
dom::ChannelInterpretation mChannelInterpretation;
ChannelCountMode mChannelCountMode;
ChannelInterpretation mChannelInterpretation;
// Whether the stream should be marked as finished as soon
// as the current time range has been computed block by block.
bool mMarkAsFinishedAfterThisBlock;

View File

@ -99,6 +99,7 @@ struct AudioChunk {
mDuration = aDuration;
mVolume = 1.0f;
}
int ChannelCount() const { return mChannelData.Length(); }
TrackTicks mDuration; // in frames within the buffer
nsRefPtr<ThreadSharedObject> mBuffer; // the buffer object whose lifetime is managed; null means data is all zeroes

View File

@ -1102,7 +1102,7 @@ MediaStreamGraphImpl::ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
if (ps) {
ps->ProduceOutput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
ps->ProcessInput(t, next, (next == aTo) ? ProcessedMediaStream::ALLOW_FINISH : 0);
}
}
t = next;
@ -1211,7 +1211,7 @@ MediaStreamGraphImpl::RunThread()
// Play stream contents.
bool allBlockedForever = true;
// True when we've done ProduceOutput for all processed streams.
// True when we've done ProcessInput for all processed streams.
bool doneAllProducing = false;
// Figure out what each stream wants to do
for (uint32_t i = 0; i < mStreams.Length(); ++i) {
@ -1237,8 +1237,8 @@ MediaStreamGraphImpl::RunThread()
ticksProcessed += TimeToTicksRoundDown(n->SampleRate(), mStateComputedTime - prevComputedTime);
doneAllProducing = true;
} else {
ps->ProduceOutput(prevComputedTime, mStateComputedTime,
ProcessedMediaStream::ALLOW_FINISH);
ps->ProcessInput(prevComputedTime, mStateComputedTime,
ProcessedMediaStream::ALLOW_FINISH);
NS_WARN_IF_FALSE(stream->mBuffer.GetEnd() >=
GraphTimeToStreamTime(stream, mStateComputedTime),
"Stream did not produce enough data");

View File

@ -906,7 +906,7 @@ protected:
/**
* This stream processes zero or more input streams in parallel to produce
* its output. The details of how the output is produced are handled by
* subclasses overriding the ProduceOutput method.
* subclasses overriding the ProcessInput method.
*/
class ProcessedMediaStream : public MediaStream {
public:
@ -962,7 +962,7 @@ public:
* This will be called on streams that have finished. Most stream types should
* just return immediately if IsFinishedOnGraphThread(), but some may wish to
* update internal state (see AudioNodeStream).
* ProduceOutput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
* ProcessInput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
* is in aFlags. (This flag will be set when aTo >= mStateComputedTime, i.e.
* when we've producing the last block of data we need to produce.) Otherwise
* we can get into a situation where we've determined the stream should not
@ -972,7 +972,7 @@ public:
enum {
ALLOW_FINISH = 0x01
};
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
/**
@ -1065,7 +1065,7 @@ public:
* Dispatches a runnable that will run on the main thread after all
* main-thread stream state has been next updated.
* Should only be called during MediaStreamListener callbacks or during
* ProcessedMediaStream::ProduceOutput().
* ProcessedMediaStream::ProcessInput().
*/
void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
{

View File

@ -39,7 +39,7 @@ public:
}
ProcessedMediaStream::RemoveInput(aPort);
}
virtual void ProduceOutput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE
virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) MOZ_OVERRIDE
{
if (IsFinishedOnGraphThread()) {
return;

View File

@ -30,9 +30,9 @@ class JSObject;
#ifdef PR_LOGGING
PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
// Arbitrary limit.
@ -40,6 +40,47 @@ static const unsigned int MAX_SOURCE_BUFFERS = 16;
namespace mozilla {
static const char* const gMediaSourceTypes[6] = {
"video/webm",
"audio/webm",
"video/mp4",
"audio/mp4",
"audio/mpeg",
nullptr
};
static nsresult
IsTypeSupported(const nsAString& aType)
{
if (aType.IsEmpty()) {
return NS_ERROR_DOM_INVALID_ACCESS_ERR;
}
// TODO: Further restrict this to formats in the spec.
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
bool found = false;
for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
found = true;
break;
}
}
if (!found) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
// Check aType against HTMLMediaElement list of MIME types. Since we've
// already restricted the container format, this acts as a specific check
// of any specified "codecs" parameter of aType.
if (dom::HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
return NS_OK;
}
namespace dom {
/* static */ already_AddRefed<MediaSource>
@ -103,7 +144,9 @@ MediaSource::SetDuration(double aDuration, ErrorResult& aRv)
already_AddRefed<SourceBuffer>
MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
{
if (!IsTypeSupportedInternal(aType, aRv)) {
nsresult rv = mozilla::IsTypeSupported(aType);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
return nullptr;
}
if (mSourceBuffers->Length() >= MAX_SOURCE_BUFFERS) {
@ -116,15 +159,15 @@ MediaSource::AddSourceBuffer(const nsAString& aType, ErrorResult& aRv)
}
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
nsRefPtr<SourceBuffer> sourceBuffer = new SourceBuffer(this, NS_ConvertUTF16toUTF8(mimeType));
mSourceBuffers->Append(sourceBuffer);
LOG(PR_LOG_DEBUG, ("%p AddSourceBuffer(Type=%s) -> %p", this,
NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get()));
MSE_DEBUG("%p AddSourceBuffer(Type=%s) -> %p", this,
NS_ConvertUTF16toUTF8(mimeType).get(), sourceBuffer.get());
return sourceBuffer.forget();
}
@ -165,21 +208,45 @@ MediaSource::EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, Er
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
EndOfStreamInternal(aError, aRv);
SetReadyState(MediaSourceReadyState::Ended);
mSourceBuffers->Ended();
if (!aError.WasPassed()) {
// TODO:
// Run duration change algorithm.
// DurationChange(highestDurationOfSourceBuffers, aRv);
// if (aRv.Failed()) {
// return;
// }
// Notify media element that all data is now available.
return;
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run resource fetch algorithm
// > HAVE_NOTHING -> run "interrupted" steps of resource fetch
break;
case MediaSourceEndOfStreamError::Decode:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run "unsupported" steps of resource fetch
// > HAVE_NOTHING -> run "corrupted" steps of resource fetch
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
}
/* static */ bool
MediaSource::IsTypeSupported(const GlobalObject& aGlobal,
const nsAString& aType)
MediaSource::IsTypeSupported(const GlobalObject&, const nsAString& aType)
{
ErrorResult unused;
return IsTypeSupportedInternal(aType, unused);
return NS_SUCCEEDED(mozilla::IsTypeSupported(aType));
}
bool
MediaSource::Attach(MediaSourceDecoder* aDecoder)
{
LOG(PR_LOG_DEBUG, ("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner()));
MSE_DEBUG("%p Attaching decoder %p owner %p", this, aDecoder, aDecoder->GetOwner());
MOZ_ASSERT(aDecoder);
if (mReadyState != MediaSourceReadyState::Closed) {
return false;
@ -193,7 +260,7 @@ MediaSource::Attach(MediaSourceDecoder* aDecoder)
void
MediaSource::Detach()
{
LOG(PR_LOG_DEBUG, ("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner()));
MSE_DEBUG("%p Detaching decoder %p owner %p", this, mDecoder.get(), mDecoder->GetOwner());
MOZ_ASSERT(mDecoder);
mDecoder->DetachMediaSource();
mDecoder = nullptr;
@ -253,14 +320,14 @@ MediaSource::SetReadyState(MediaSourceReadyState aState)
void
MediaSource::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to MediaSource", this, aName));
MSE_DEBUG("%p Dispatching event %s to MediaSource", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
MediaSource::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to MediaSource", this, aName));
MSE_DEBUG("%p Queuing event %s to MediaSource", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
@ -283,82 +350,6 @@ MediaSource::DurationChange(double aNewDuration, ErrorResult& aRv)
// TODO: Update media element's duration and run element's duration change algorithm.
}
void
MediaSource::EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv)
{
SetReadyState(MediaSourceReadyState::Ended);
mSourceBuffers->Ended();
if (!aError.WasPassed()) {
// TODO:
// Run duration change algorithm.
// DurationChange(highestDurationOfSourceBuffers, aRv);
// if (aRv.Failed()) {
// return;
// }
// Notify media element that all data is now available.
return;
}
switch (aError.Value()) {
case MediaSourceEndOfStreamError::Network:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run resource fetch algorithm
// > HAVE_NOTHING -> run "interrupted" steps of resource fetch
break;
case MediaSourceEndOfStreamError::Decode:
// TODO: If media element has a readyState of:
// HAVE_NOTHING -> run "unsupported" steps of resource fetch
// > HAVE_NOTHING -> run "corrupted" steps of resource fetch
break;
default:
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
}
}
static const char* const gMediaSourceTypes[6] = {
"video/webm",
"audio/webm",
"video/mp4",
"audio/mp4",
"audio/mpeg",
nullptr
};
/* static */ bool
MediaSource::IsTypeSupportedInternal(const nsAString& aType, ErrorResult& aRv)
{
if (aType.IsEmpty()) {
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
return false;
}
// TODO: Further restrict this to formats in the spec.
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
bool found = false;
for (uint32_t i = 0; gMediaSourceTypes[i]; ++i) {
if (mimeType.EqualsASCII(gMediaSourceTypes[i])) {
found = true;
break;
}
}
if (!found) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
// Check aType against HTMLMediaElement list of MIME types. Since we've
// already restricted the container format, this acts as a specific check
// of any specified "codecs" parameter of aType.
if (HTMLMediaElement::GetCanPlay(aType) == CANPLAY_NO) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return false;
}
return true;
}
nsPIDOMWindow*
MediaSource::GetParentObject() const
{

View File

@ -60,8 +60,7 @@ public:
void RemoveSourceBuffer(SourceBuffer& aSourceBuffer, ErrorResult& aRv);
void EndOfStream(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv);
static bool IsTypeSupported(const GlobalObject& aGlobal,
const nsAString& aType);
static bool IsTypeSupported(const GlobalObject&, const nsAString& aType);
/** End WebIDL Methods. */
NS_DECL_ISUPPORTS_INHERITED
@ -98,9 +97,6 @@ private:
void QueueAsyncSimpleEvent(const char* aName);
void DurationChange(double aNewDuration, ErrorResult& aRv);
void EndOfStreamInternal(const Optional<MediaSourceEndOfStreamError>& aError, ErrorResult& aRv);
static bool IsTypeSupportedInternal(const nsAString& aType, ErrorResult& aRv);
double mDuration;

View File

@ -23,9 +23,9 @@
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -202,7 +202,7 @@ MediaSourceDecoder::CreateSubDecoder(const nsACString& aType)
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mDecoders.AppendElement(decoder);
mReaders.AppendElement(reader);
LOG(PR_LOG_DEBUG, ("Registered subdecoder %p subreader %p", decoder.get(), reader.get()));
MSE_DEBUG("Registered subdecoder %p subreader %p", decoder.get(), reader.get());
mon.NotifyAll();
decoder->SetReader(reader.forget());
@ -221,7 +221,7 @@ MediaSourceReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags)
MediaDecoderReader* reader = readers[i];
MediaInfo mi;
nsresult rv = reader->ReadMetadata(&mi, aTags);
LOG(PR_LOG_DEBUG, ("ReadMetadata on SB reader %p", reader));
MSE_DEBUG("ReadMetadata on SB reader %p", reader);
if (NS_FAILED(rv)) {
return rv;
}

View File

@ -26,9 +26,9 @@ class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -289,14 +289,14 @@ SourceBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
void
SourceBuffer::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to SourceBuffer", this, aName));
MSE_DEBUG("%p Dispatching event %s to SourceBuffer", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
SourceBuffer::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to SourceBuffer", this, aName));
MSE_DEBUG("%p Queuing event %s to SourceBuffer", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBuffer>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}
@ -339,7 +339,7 @@ SourceBuffer::AppendData(const uint8_t* aData, uint32_t aLength, ErrorResult& aR
}
// TODO: Run coded frame eviction algorithm.
// TODO: Test buffer full flag.
LOG(PR_LOG_DEBUG, ("%p Append(ArrayBuffer=%u)", this, aLength));
MSE_DEBUG("%p Append(ArrayBuffer=%u)", this, aLength);
StartUpdating();
// XXX: For future reference: NDA call must run on the main thread.
mDecoder->NotifyDataArrived(reinterpret_cast<const char*>(aData),

View File

@ -22,9 +22,9 @@ class JSObject;
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -122,14 +122,14 @@ SourceBufferList::Ended()
void
SourceBufferList::DispatchSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Dispatching event %s to SourceBufferList", this, aName));
MSE_DEBUG("%p Dispatching event %s to SourceBufferList", this, aName);
DispatchTrustedEvent(NS_ConvertUTF8toUTF16(aName));
}
void
SourceBufferList::QueueAsyncSimpleEvent(const char* aName)
{
LOG(PR_LOG_DEBUG, ("%p Queuing event %s to SourceBufferList", this, aName));
MSE_DEBUG("%p Queuing event %s to SourceBufferList", this, aName);
nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<SourceBufferList>(this, aName);
NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
}

View File

@ -16,9 +16,9 @@
#ifdef PR_LOGGING
extern PRLogModuleInfo* gMediaSourceLog;
#define LOG(type, msg) PR_LOG(gMediaSourceLog, type, msg)
#define MSE_DEBUG(...) PR_LOG(gMediaSourceLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
#define LOG(type, msg)
#define MSE_DEBUG(...)
#endif
namespace mozilla {
@ -33,7 +33,7 @@ nsresult
SourceBufferResource::Close()
{
ReentrantMonitorAutoEnter mon(mMonitor);
LOG(PR_LOG_DEBUG, ("%p SBR::Close", this));
MSE_DEBUG("%p SBR::Close", this);
//MOZ_ASSERT(!mClosed);
mClosed = true;
mon.NotifyAll();
@ -49,20 +49,20 @@ SourceBufferResource::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
while (blockingRead &&
!mEnded &&
mOffset + aCount > static_cast<uint64_t>(GetLength())) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read waiting for data", this));
MSE_DEBUG("%p SBR::Read waiting for data", this);
mon.Wait();
}
uint32_t available = GetLength() - mOffset;
uint32_t count = std::min(aCount, available);
if (!PR_GetEnv("MOZ_QUIET")) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read aCount=%u length=%u offset=%u "
"available=%u count=%u, blocking=%d bufComplete=%d",
this, aCount, GetLength(), mOffset, available, count,
blockingRead, mEnded));
MSE_DEBUG("%p SBR::Read aCount=%u length=%u offset=%u "
"available=%u count=%u, blocking=%d bufComplete=%d",
this, aCount, GetLength(), mOffset, available, count,
blockingRead, mEnded);
}
if (available == 0) {
LOG(PR_LOG_DEBUG, ("%p SBR::Read EOF", this));
MSE_DEBUG("%p SBR::Read EOF", this);
*aBytes = 0;
return NS_OK;
}
@ -160,7 +160,7 @@ SourceBufferResource::Ended()
SourceBufferResource::~SourceBufferResource()
{
MOZ_COUNT_DTOR(SourceBufferResource);
LOG(PR_LOG_DEBUG, ("%p SBR::~SBR", this));
MSE_DEBUG("%p SBR::~SBR", this);
}
SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
@ -173,7 +173,7 @@ SourceBufferResource::SourceBufferResource(nsIPrincipal* aPrincipal,
, mEnded(false)
{
MOZ_COUNT_CTOR(SourceBufferResource);
LOG(PR_LOG_DEBUG, ("%p SBR::SBR()", this));
MSE_DEBUG("%p SBR::SBR()", this);
}
} // namespace mozilla

View File

@ -57,10 +57,10 @@ public:
MOZ_ASSERT(NS_IsMainThread());
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
*aOutput = aInput;

View File

@ -423,10 +423,10 @@ public:
UpdateResampler(outRate, aChannels);
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
if (!mBuffer || !mBufferEnd) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);

View File

@ -52,10 +52,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
// Do this just for the sake of political correctness; this output
// will not go anywhere.
@ -165,10 +165,10 @@ public:
{
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
*aOutput = aInput;
aOutput->mVolume *= mVolume;

View File

@ -11,19 +11,19 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED_3(AudioProcessingEvent, nsDOMEvent,
NS_IMPL_CYCLE_COLLECTION_INHERITED_3(AudioProcessingEvent, Event,
mInputBuffer, mOutputBuffer, mNode)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioProcessingEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
NS_IMPL_ADDREF_INHERITED(AudioProcessingEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(AudioProcessingEvent, nsDOMEvent)
NS_IMPL_ADDREF_INHERITED(AudioProcessingEvent, Event)
NS_IMPL_RELEASE_INHERITED(AudioProcessingEvent, Event)
AudioProcessingEvent::AudioProcessingEvent(ScriptProcessorNode* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent)
: nsDOMEvent(aOwner, aPresContext, aEvent)
: Event(aOwner, aPresContext, aEvent)
, mPlaybackTime(0.0)
, mNode(aOwner)
{

View File

@ -7,14 +7,14 @@
#ifndef AudioProcessingEvent_h_
#define AudioProcessingEvent_h_
#include "nsDOMEvent.h"
#include "AudioBuffer.h"
#include "ScriptProcessorNode.h"
#include "mozilla/dom/Event.h"
namespace mozilla {
namespace dom {
class AudioProcessingEvent : public nsDOMEvent
class AudioProcessingEvent : public Event
{
public:
AudioProcessingEvent(ScriptProcessorNode* aOwner,
@ -22,8 +22,8 @@ public:
WidgetEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_TO_NSDOMEVENT
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioProcessingEvent, nsDOMEvent)
NS_FORWARD_TO_EVENT
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioProcessingEvent, Event)
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;

View File

@ -137,10 +137,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
float inputBuffer[WEBAUDIO_BLOCK_SIZE];

View File

@ -23,10 +23,10 @@ public:
MOZ_ASSERT(NS_IsMainThread());
}
virtual void ProduceAudioBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished) MOZ_OVERRIDE
{
MOZ_ASSERT(aInput.Length() >= 1, "Should have one or more input ports");

View File

@ -23,10 +23,10 @@ public:
MOZ_ASSERT(NS_IsMainThread());
}
virtual void ProduceAudioBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlocksOnPorts(AudioNodeStream* aStream,
const OutputChunks& aInput,
OutputChunks& aOutput,
bool* aFinished) MOZ_OVERRIDE
{
MOZ_ASSERT(aInput.Length() == 1, "Should only have one input port");

View File

@ -101,10 +101,10 @@ public:
mNormalize, mSampleRate);
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
if (!mReverb) {
*aOutput = aInput;

View File

@ -0,0 +1,249 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "DelayBuffer.h"
#include "mozilla/PodOperations.h"
#include "AudioChannelFormat.h"
#include "AudioNodeEngine.h"
namespace mozilla {
void
DelayBuffer::Write(const AudioChunk& aInputChunk)
{
// We must have a reference to the buffer if there are channels
MOZ_ASSERT(aInputChunk.IsNull() == !aInputChunk.mChannelData.Length());
if (!EnsureBuffer()) {
return;
}
if (mCurrentChunk == mLastReadChunk) {
mLastReadChunk = -1; // invalidate cache
}
mChunks[mCurrentChunk] = aInputChunk;
}
void
DelayBuffer::Read(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
AudioChunk* aOutputChunk,
ChannelInterpretation aChannelInterpretation)
{
int chunkCount = mChunks.Length();
if (!chunkCount) {
aOutputChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
return;
}
// Find the maximum number of contributing channels to determine the output
// channel count that retains all signal information. Buffered blocks will
// be upmixed if necessary.
//
// First find the range of "delay" offsets backwards from the current
// position. Note that these may be negative for frames that are after the
// current position (including i).
double minDelay = aPerFrameDelays[0];
double maxDelay = minDelay;
for (unsigned i = 1; i < WEBAUDIO_BLOCK_SIZE; ++i) {
minDelay = std::min(minDelay, aPerFrameDelays[i] - i);
maxDelay = std::max(maxDelay, aPerFrameDelays[i] - i);
}
// Now find the chunks touched by this range and check their channel counts.
int oldestChunk = ChunkForDelay(int(maxDelay) + 1);
int youngestChunk = ChunkForDelay(minDelay);
uint32_t channelCount = 0;
for (int i = oldestChunk; true; i = (i + 1) % chunkCount) {
channelCount = GetAudioChannelsSuperset(channelCount,
mChunks[i].ChannelCount());
if (i == youngestChunk) {
break;
}
}
if (channelCount) {
AllocateAudioBlock(channelCount, aOutputChunk);
ReadChannels(aPerFrameDelays, aOutputChunk,
0, channelCount, aChannelInterpretation);
} else {
aOutputChunk->SetNull(WEBAUDIO_BLOCK_SIZE);
}
// Remember currentDelayFrames for the next ProcessBlock call
mCurrentDelay = aPerFrameDelays[WEBAUDIO_BLOCK_SIZE - 1];
}
void
DelayBuffer::ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk, uint32_t aChannel,
ChannelInterpretation aChannelInterpretation)
{
if (!mChunks.Length()) {
float* outputChannel = static_cast<float*>
(const_cast<void*>(aOutputChunk->mChannelData[aChannel]));
PodZero(outputChannel, WEBAUDIO_BLOCK_SIZE);
return;
}
ReadChannels(aPerFrameDelays, aOutputChunk,
aChannel, 1, aChannelInterpretation);
}
void
DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk,
uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
ChannelInterpretation aChannelInterpretation)
{
uint32_t totalChannelCount = aOutputChunk->mChannelData.Length();
uint32_t readChannelsEnd = aFirstChannel + aNumChannelsToRead;
MOZ_ASSERT(readChannelsEnd <= totalChannelCount);
if (mUpmixChannels.Length() != totalChannelCount) {
mLastReadChunk = -1; // invalidate cache
}
float* const* outputChannels = reinterpret_cast<float* const*>
(const_cast<void* const*>(aOutputChunk->mChannelData.Elements()));
for (uint32_t channel = aFirstChannel;
channel < readChannelsEnd; ++channel) {
PodZero(outputChannels[channel], WEBAUDIO_BLOCK_SIZE);
}
for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
double currentDelay = aPerFrameDelays[i];
MOZ_ASSERT(currentDelay >= 0.0);
MOZ_ASSERT(currentDelay <= static_cast<double>(mMaxDelayTicks));
// Interpolate two input frames in case the read position does not match
// an integer index.
// Use the larger delay, for the older frame, first, as this is more
// likely to use the cached upmixed channel arrays.
int floorDelay = int(currentDelay);
double interpolationFactor = currentDelay - floorDelay;
int positions[2];
positions[1] = PositionForDelay(floorDelay) + i;
positions[0] = positions[1] - 1;
for (unsigned tick = 0; tick < ArrayLength(positions); ++tick) {
int readChunk = ChunkForPosition(positions[tick]);
// mVolume is not set on default initialized chunks so handle null
// chunks specially.
if (!mChunks[readChunk].IsNull()) {
int readOffset = OffsetForPosition(positions[tick]);
UpdateUpmixChannels(readChunk, totalChannelCount,
aChannelInterpretation);
double multiplier = interpolationFactor * mChunks[readChunk].mVolume;
for (uint32_t channel = aFirstChannel;
channel < readChannelsEnd; ++channel) {
outputChannels[channel][i] += multiplier *
static_cast<const float*>(mUpmixChannels[channel])[readOffset];
}
}
interpolationFactor = 1.0 - interpolationFactor;
}
}
}
void
DelayBuffer::Read(double aDelayTicks, AudioChunk* aOutputChunk,
ChannelInterpretation aChannelInterpretation)
{
const bool firstTime = mCurrentDelay < 0.0;
double currentDelay = firstTime ? aDelayTicks : mCurrentDelay;
double computedDelay[WEBAUDIO_BLOCK_SIZE];
for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
// If the value has changed, smoothly approach it
currentDelay += (aDelayTicks - currentDelay) * mSmoothingRate;
computedDelay[i] = currentDelay;
}
Read(computedDelay, aOutputChunk, aChannelInterpretation);
}
bool
DelayBuffer::EnsureBuffer()
{
if (mChunks.Length() == 0) {
// The length of the buffer is at least one block greater than the maximum
// delay so that writing an input block does not overwrite the block that
// would subsequently be read at maximum delay. Also round up to the next
// block size, so that no block of writes will need to wrap.
const int chunkCount = (mMaxDelayTicks + 2 * WEBAUDIO_BLOCK_SIZE - 1) >>
WEBAUDIO_BLOCK_SIZE_BITS;
if (!mChunks.SetLength(chunkCount)) {
return false;
}
mLastReadChunk = -1;
}
return true;
}
int
DelayBuffer::PositionForDelay(int aDelay) {
// Adding mChunks.Length() keeps integers positive for defined and
// appropriate bitshift, remainder, and bitwise operations.
return ((mCurrentChunk + mChunks.Length()) * WEBAUDIO_BLOCK_SIZE) - aDelay;
}
int
DelayBuffer::ChunkForPosition(int aPosition)
{
MOZ_ASSERT(aPosition >= 0);
return (aPosition >> WEBAUDIO_BLOCK_SIZE_BITS) % mChunks.Length();
}
int
DelayBuffer::OffsetForPosition(int aPosition)
{
MOZ_ASSERT(aPosition >= 0);
return aPosition & (WEBAUDIO_BLOCK_SIZE - 1);
}
int
DelayBuffer::ChunkForDelay(int aDelay)
{
return ChunkForPosition(PositionForDelay(aDelay));
}
void
DelayBuffer::UpdateUpmixChannels(int aNewReadChunk, uint32_t aChannelCount,
ChannelInterpretation aChannelInterpretation)
{
if (aNewReadChunk == mLastReadChunk) {
MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount);
return;
}
static const float silenceChannel[WEBAUDIO_BLOCK_SIZE] = {};
mLastReadChunk = aNewReadChunk;
// Missing assignment operator is bug 976927
mUpmixChannels.ReplaceElementsAt(0, mUpmixChannels.Length(),
mChunks[aNewReadChunk].mChannelData);
MOZ_ASSERT(mUpmixChannels.Length() <= aChannelCount);
if (mUpmixChannels.Length() < aChannelCount) {
if (aChannelInterpretation == ChannelInterpretation::Speakers) {
AudioChannelsUpMix(&mUpmixChannels, aChannelCount, silenceChannel);
MOZ_ASSERT(mUpmixChannels.Length() == aChannelCount,
"We called GetAudioChannelsSuperset to avoid this");
} else {
// Fill up the remaining channels with zeros
for (uint32_t channel = mUpmixChannels.Length();
channel < aChannelCount; ++channel) {
mUpmixChannels.AppendElement(silenceChannel);
}
}
}
}
} // mozilla

View File

@ -0,0 +1,96 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 DelayBuffer_h_
#define DelayBuffer_h_
#include "nsTArray.h"
#include "AudioSegment.h"
#include "mozilla/dom/AudioNodeBinding.h" // for ChannelInterpretation
namespace mozilla {
class DelayBuffer {
typedef dom::ChannelInterpretation ChannelInterpretation;
public:
// See WebAudioUtils::ComputeSmoothingRate() for frame to frame exponential
// |smoothingRate| multiplier.
DelayBuffer(int aMaxDelayTicks, double aSmoothingRate)
: mSmoothingRate(aSmoothingRate)
, mCurrentDelay(-1.0)
, mMaxDelayTicks(aMaxDelayTicks)
, mCurrentChunk(0)
// mLastReadChunk is initialized in EnsureBuffer
{
}
// Write a WEBAUDIO_BLOCK_SIZE block for aChannelCount channels.
void Write(const AudioChunk& aInputChunk);
// Read a block with an array of delays, in ticks, for each sample frame.
// Each delay must be > 0 and < MaxDelayTicks().
void Read(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
AudioChunk* aOutputChunk,
ChannelInterpretation aChannelInterpretation);
// Read a block with a constant delay, which will be smoothed with the
// previous delay. The delay must be > 0 and < MaxDelayTicks().
void Read(double aDelayTicks, AudioChunk* aOutputChunk,
ChannelInterpretation aChannelInterpretation);
// Read into one of the channels of aOutputChunk, given an array of
// delays in ticks. This is useful when delays are different on different
// channels. aOutputChunk must have already been allocated with at least as
// many channels as were in any of the blocks passed to Write().
void ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk, uint32_t aChannel,
ChannelInterpretation aChannelInterpretation);
// Advance the buffer pointer
void NextBlock()
{
mCurrentChunk = (mCurrentChunk + 1) % mChunks.Length();
}
void Reset() {
mChunks.Clear();
mCurrentDelay = -1.0;
};
int MaxDelayTicks() const { return mMaxDelayTicks; }
private:
void ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk,
uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
ChannelInterpretation aChannelInterpretation);
bool EnsureBuffer();
int PositionForDelay(int aDelay);
int ChunkForPosition(int aPosition);
int OffsetForPosition(int aPosition);
int ChunkForDelay(int aDelay);
void UpdateUpmixChannels(int aNewReadChunk, uint32_t channelCount,
ChannelInterpretation aChannelInterpretation);
// Circular buffer for capturing delayed samples.
FallibleTArray<AudioChunk> mChunks;
// Cache upmixed channel arrays.
nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> mUpmixChannels;
double mSmoothingRate;
// Current delay, in fractional ticks
double mCurrentDelay;
// Maximum delay, in ticks
int mMaxDelayTicks;
// The current position in the circular buffer. The next write will be to
// this chunk, and the next read may begin before this chunk.
int mCurrentChunk;
// The chunk owning the pointers in mUpmixChannels
int mLastReadChunk;
};
} // mozilla
#endif // DelayBuffer_h_

View File

@ -10,7 +10,7 @@
#include "AudioNodeStream.h"
#include "AudioDestinationNode.h"
#include "WebAudioUtils.h"
#include "DelayProcessor.h"
#include "DelayBuffer.h"
#include "PlayingRefChangeHandler.h"
namespace mozilla {
@ -30,16 +30,17 @@ class DelayNodeEngine : public AudioNodeEngine
typedef PlayingRefChangeHandler PlayingRefChanged;
public:
DelayNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
int aMaxDelayFrames)
int aMaxDelayTicks)
: AudioNodeEngine(aNode)
, mSource(nullptr)
, mDestination(static_cast<AudioNodeStream*> (aDestination->Stream()))
// Keep the default value in sync with the default value in DelayNode::DelayNode.
, mDelay(0.f)
// Use a smoothing range of 20ms
, mProcessor(aMaxDelayFrames,
WebAudioUtils::ComputeSmoothingRate(0.02,
mDestination->SampleRate()))
, mBuffer(aMaxDelayTicks,
WebAudioUtils::ComputeSmoothingRate(0.02,
mDestination->SampleRate()))
, mLastOutputPosition(-1)
, mLeftOverData(INT32_MIN)
{
}
@ -72,18 +73,14 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
MOZ_ASSERT(mSource == aStream, "Invalid source stream");
MOZ_ASSERT(aStream->SampleRate() == mDestination->SampleRate());
const uint32_t numChannels = aInput.IsNull() ?
mProcessor.BufferChannelCount() :
aInput.mChannelData.Length();
if (!aInput.IsNull()) {
if (mLeftOverData <= 0) {
nsRefPtr<PlayingRefChanged> refchanged =
@ -91,14 +88,14 @@ public:
aStream->Graph()->
DispatchToMainThreadAfterStreamStateUpdate(refchanged.forget());
}
mLeftOverData = mProcessor.MaxDelayFrames();
mLeftOverData = mBuffer.MaxDelayTicks();
} else if (mLeftOverData > 0) {
mLeftOverData -= WEBAUDIO_BLOCK_SIZE;
} else {
if (mLeftOverData != INT32_MIN) {
mLeftOverData = INT32_MIN;
// Delete our buffered data now we no longer need it
mProcessor.Reset();
mBuffer.Reset();
nsRefPtr<PlayingRefChanged> refchanged =
new PlayingRefChanged(aStream, PlayingRefChanged::RELEASE);
@ -109,56 +106,60 @@ public:
return;
}
AllocateAudioBlock(numChannels, aOutput);
mBuffer.Write(aInput);
AudioChunk input = aInput;
if (!aInput.IsNull() && aInput.mVolume != 1.0f) {
// Pre-multiply the input's volume
AllocateAudioBlock(numChannels, &input);
for (uint32_t i = 0; i < numChannels; ++i) {
const float* src = static_cast<const float*>(aInput.mChannelData[i]);
float* dest = static_cast<float*>(const_cast<void*>(input.mChannelData[i]));
AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest);
}
UpdateOutputBlock(aOutput);
mBuffer.NextBlock();
}
void UpdateOutputBlock(AudioChunk* aOutput)
{
TrackTicks tick = mSource->GetCurrentPosition();
if (tick == mLastOutputPosition) {
return; // mLastChunks is already set on the stream
}
const float* const* inputChannels = input.IsNull() ? nullptr :
reinterpret_cast<const float* const*>(input.mChannelData.Elements());
float* const* outputChannels = reinterpret_cast<float* const*>
(const_cast<void* const*>(aOutput->mChannelData.Elements()));
bool inCycle = aStream->AsProcessedStream()->InCycle();
double sampleRate = aStream->SampleRate();
mLastOutputPosition = tick;
bool inCycle = mSource->AsProcessedStream()->InCycle();
double minDelay = inCycle ? static_cast<double>(WEBAUDIO_BLOCK_SIZE) : 0.0;
double maxDelay = mBuffer.MaxDelayTicks();
double sampleRate = mSource->SampleRate();
ChannelInterpretation channelInterpretation =
mSource->GetChannelInterpretation();
if (mDelay.HasSimpleValue()) {
// If this DelayNode is in a cycle, make sure the delay value is at least
// one block.
float delayFrames = mDelay.GetValue() * sampleRate;
float delayFramesClamped = inCycle ? std::max(static_cast<float>(WEBAUDIO_BLOCK_SIZE), delayFrames) :
delayFrames;
mProcessor.Process(delayFramesClamped, inputChannels, outputChannels,
numChannels, WEBAUDIO_BLOCK_SIZE);
double delayFrames = mDelay.GetValue() * sampleRate;
double delayFramesClamped = clamped(delayFrames, minDelay, maxDelay);
mBuffer.Read(delayFramesClamped, aOutput, channelInterpretation);
} else {
// Compute the delay values for the duration of the input AudioChunk
// If this DelayNode is in a cycle, make sure the delay value is at least
// one block.
double computedDelay[WEBAUDIO_BLOCK_SIZE];
TrackTicks tick = aStream->GetCurrentPosition();
for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) {
float delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
float delayAtTickClamped = inCycle ? std::max(static_cast<float>(WEBAUDIO_BLOCK_SIZE), delayAtTick) :
delayAtTick;
double delayAtTick = mDelay.GetValueAtTime(tick, counter) * sampleRate;
double delayAtTickClamped = clamped(delayAtTick, minDelay, maxDelay);
computedDelay[counter] = delayAtTickClamped;
}
mProcessor.Process(computedDelay, inputChannels, outputChannels,
numChannels, WEBAUDIO_BLOCK_SIZE);
mBuffer.Read(computedDelay, aOutput, channelInterpretation);
}
}
virtual void ProduceBlockBeforeInput(AudioChunk* aOutput) MOZ_OVERRIDE
{
if (mLeftOverData <= 0) {
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
} else {
UpdateOutputBlock(aOutput);
}
}
AudioNodeStream* mSource;
AudioNodeStream* mDestination;
AudioParamTimeline mDelay;
DelayProcessor mProcessor;
DelayBuffer mBuffer;
TrackTicks mLastOutputPosition;
// How much data we have in our buffer which needs to be flushed out when our inputs
// finish.
int32_t mLeftOverData;

View File

@ -1,127 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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/. */
#include "DelayProcessor.h"
#include "mozilla/PodOperations.h"
#include "AudioSegment.h"
namespace mozilla {
void
DelayProcessor::Process(const double *aPerFrameDelays,
const float* const* aInputChannels,
float* const* aOutputChannels,
int aChannelCount, int aFramesToProcess)
{
if (!EnsureBuffer(aChannelCount)) {
for (int channel = 0; channel < aChannelCount; ++channel) {
PodZero(aOutputChannels[channel], aFramesToProcess);
}
return;
}
for (int channel = 0; channel < aChannelCount; ++channel) {
double currentDelayFrames = mCurrentDelay;
int writeIndex = mWriteIndex;
float* buffer = mBuffer[channel].Elements();
const uint32_t bufferLength = mBuffer[channel].Length();
const float* input = aInputChannels ? aInputChannels[channel] : nullptr;
float* output = aOutputChannels[channel];
for (int i = 0; i < aFramesToProcess; ++i) {
currentDelayFrames = clamped(aPerFrameDelays[i],
0.0, static_cast<double>(mMaxDelayFrames));
// Write the input sample to the correct location in our buffer
buffer[writeIndex] = input ? input[i] : 0.0f;
// Now, determine the correct read position. We adjust the read position to be
// from currentDelayFrames frames in the past. We also interpolate the two input
// frames in case the read position does not match an integer index.
double readPosition = writeIndex + bufferLength - currentDelayFrames;
if (readPosition >= bufferLength) {
readPosition -= bufferLength;
}
MOZ_ASSERT(readPosition >= 0.0, "Why are we reading before the beginning of the buffer?");
// Here is a the reason why readIndex1 and readIndex will never be out
// of bounds. The maximum value for bufferLength is
// 180 * AudioContext.samplerate (see AudioContext::CreateDelay). The
// maximum value for mCurrentDelay is 180.0, so initially readPosition
// cannot be more than bufferLength + a fraction less than 1. Then we
// take care of that case by subtracting bufferLength from it if needed.
// So, if |bufferLength-readPosition<1.0|, readIndex1 will end up being
// zero. If |1.0<=bufferLength-readPosition<2.0|, readIndex1 will be
// bufferLength-1 and readIndex2 will be 0.
int readIndex1 = int(readPosition);
int readIndex2 = (readIndex1 + 1) % bufferLength;
double interpolationFactor = readPosition - readIndex1;
output[i] = (1.0 - interpolationFactor) * buffer[readIndex1] +
interpolationFactor * buffer[readIndex2];
writeIndex = (writeIndex + 1) % bufferLength;
}
// Remember currentDelayFrames and writeIndex for the next ProduceAudioBlock
// call when processing the last channel.
if (channel == aChannelCount - 1) {
mCurrentDelay = currentDelayFrames;
mWriteIndex = writeIndex;
}
}
}
void
DelayProcessor::Process(double aDelayFrames, const float* const* aInputChannels,
float* const* aOutputChannels, int aChannelCount,
int aFramesToProcess)
{
const bool firstTime = !mBuffer.Length();
double currentDelay = firstTime ? aDelayFrames : mCurrentDelay;
nsAutoTArray<double, WEBAUDIO_BLOCK_SIZE> computedDelay;
computedDelay.SetLength(aFramesToProcess);
for (int i = 0; i < aFramesToProcess; ++i) {
// If the value has changed, smoothly approach it
currentDelay += (aDelayFrames - currentDelay) * mSmoothingRate;
computedDelay[i] = currentDelay;
}
Process(computedDelay.Elements(), aInputChannels, aOutputChannels,
aChannelCount, aFramesToProcess);
}
bool
DelayProcessor::EnsureBuffer(uint32_t aNumberOfChannels)
{
if (aNumberOfChannels == 0) {
return false;
}
if (mBuffer.Length() == 0) {
if (!mBuffer.SetLength(aNumberOfChannels)) {
return false;
}
// The length of the buffer is one greater than the maximum delay so that
// writing an input frame does not overwrite the frame that would
// subsequently be read at maximum delay.
const int numFrames = mMaxDelayFrames + 1;
for (uint32_t channel = 0; channel < aNumberOfChannels; ++channel) {
if (!mBuffer[channel].SetLength(numFrames)) {
return false;
}
PodZero(mBuffer[channel].Elements(), numFrames);
}
} else if (mBuffer.Length() != aNumberOfChannels) {
// TODO: Handle changes in the channel count
return false;
}
return true;
}
} // mozilla

View File

@ -1,60 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 DelayProcessor_h_
#define DelayProcessor_h_
#include "nsTArray.h"
namespace mozilla {
class DelayProcessor {
public:
// See WebAudioUtils::ComputeSmoothingRate() for frame to frame exponential
// |smoothingRate| multiplier.
DelayProcessor(int aMaxDelayFrames, double aSmoothingRate)
: mSmoothingRate(aSmoothingRate)
, mCurrentDelay(0.)
, mMaxDelayFrames(aMaxDelayFrames)
, mWriteIndex(0)
{
}
// Process with an array of delays, in frames, for each frame.
void Process(const double *aPerFrameDelays,
const float* const* aInputChannels,
float* const* aOutputChannels,
int aChannelCount, int aFramesToProcess);
// Process with a constant delay, which will be smoothed with the previous
// delay.
void Process(double aDelayFrames, const float* const* aInputChannels,
float* const* aOutputChannels, int aChannelCount,
int aFramesToProcess);
void Reset() { mBuffer.Clear(); };
int MaxDelayFrames() const { return mMaxDelayFrames; }
int BufferChannelCount() const { return mBuffer.Length(); }
private:
bool EnsureBuffer(uint32_t aNumberOfChannels);
// Circular buffer for capturing delayed samples.
AutoFallibleTArray<FallibleTArray<float>, 2> mBuffer;
double mSmoothingRate;
// Current delay, in fractional frames
double mCurrentDelay;
// Maximum delay, in frames
int mMaxDelayFrames;
// Write index for the buffer, to write the frames to the correct index of the buffer
// given the current delay.
int mWriteIndex;
};
} // mozilla
#endif // DelayProcessor_h_

View File

@ -93,10 +93,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
if (aInput.IsNull()) {
// Just output silence

View File

@ -58,10 +58,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
MOZ_ASSERT(mSource == aStream, "Invalid source stream");

View File

@ -36,10 +36,10 @@ public:
MOZ_ASSERT(mOutputStream);
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
*aOutput = aInput;
StreamBuffer::Track* track = mOutputStream->EnsureTrack(MEDIA_STREAM_DEST_TRACK_ID,

View File

@ -11,19 +11,19 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(OfflineAudioCompletionEvent, nsDOMEvent,
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(OfflineAudioCompletionEvent, Event,
mRenderedBuffer)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OfflineAudioCompletionEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
NS_INTERFACE_MAP_END_INHERITING(Event)
NS_IMPL_ADDREF_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
NS_IMPL_ADDREF_INHERITED(OfflineAudioCompletionEvent, Event)
NS_IMPL_RELEASE_INHERITED(OfflineAudioCompletionEvent, Event)
OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(AudioContext* aOwner,
nsPresContext* aPresContext,
WidgetEvent* aEvent)
: nsDOMEvent(aOwner, aPresContext, aEvent)
: Event(aOwner, aPresContext, aEvent)
{
SetIsDOMBinding();
}

View File

@ -7,15 +7,15 @@
#ifndef OfflineAudioCompletionEvent_h_
#define OfflineAudioCompletionEvent_h_
#include "nsDOMEvent.h"
#include "AudioBuffer.h"
#include "mozilla/dom/Event.h"
namespace mozilla {
namespace dom {
class AudioContext;
class OfflineAudioCompletionEvent : public nsDOMEvent
class OfflineAudioCompletionEvent : public Event
{
public:
OfflineAudioCompletionEvent(AudioContext* aOwner,
@ -23,8 +23,8 @@ public:
WidgetEvent* aEvent);
NS_DECL_ISUPPORTS_INHERITED
NS_FORWARD_TO_NSDOMEVENT
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
NS_FORWARD_TO_EVENT
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OfflineAudioCompletionEvent, Event)
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;

View File

@ -410,10 +410,10 @@ public:
aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
MOZ_ASSERT(mSource == aStream, "Invalid source stream");

View File

@ -133,10 +133,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool *aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool *aFinished) MOZ_OVERRIDE
{
if (aInput.IsNull()) {
// mLeftOverData != INT_MIN means that the panning model was HRTF and a
@ -279,8 +279,6 @@ void
PannerNodeEngine::HRTFPanningFunction(const AudioChunk& aInput,
AudioChunk* aOutput)
{
int numChannels = aInput.mChannelData.Length();
// The output of this node is always stereo, no matter what the inputs are.
AllocateAudioBlock(2, aOutput);
@ -289,20 +287,9 @@ PannerNodeEngine::HRTFPanningFunction(const AudioChunk& aInput,
AudioChunk input = aInput;
// Gain is applied before the delay and convolution of the HRTF
if (!input.IsNull()) {
float gain = ComputeConeGain() * ComputeDistanceGain() * aInput.mVolume;
if (gain != 1.0f) {
AllocateAudioBlock(numChannels, &input);
for (int i = 0; i < numChannels; ++i) {
const float* src = static_cast<const float*>(aInput.mChannelData[i]);
float* dest =
static_cast<float*>(const_cast<void*>(input.mChannelData[i]));
AudioBlockCopyChannelWithScale(src, gain, dest);
}
}
}
input.mVolume *= ComputeConeGain() * ComputeDistanceGain();
mHRTFPanner->pan(azimuth, elevation, &input, aOutput, WEBAUDIO_BLOCK_SIZE);
mHRTFPanner->pan(azimuth, elevation, &input, aOutput);
}
void

View File

@ -239,10 +239,10 @@ public:
mSource = aSource;
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished) MOZ_OVERRIDE
{
MutexAutoLock lock(NodeMutex());

View File

@ -202,10 +202,10 @@ public:
}
}
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
virtual void ProcessBlock(AudioNodeStream* aStream,
const AudioChunk& aInput,
AudioChunk* aOutput,
bool* aFinished)
{
uint32_t channelCount = aInput.mChannelData.Length();
if (!mCurve.Length() || !channelCount) {

View File

@ -27,11 +27,10 @@
#include "FFTConvolver.h"
#include "HRTFDatabase.h"
#include "WebAudioUtils.h"
using namespace std;
using namespace mozilla;
using mozilla::dom::WebAudioUtils;
using dom::ChannelInterpretation;
namespace WebCore {
@ -40,7 +39,7 @@ namespace WebCore {
const double MaxDelayTimeSeconds = 0.002;
const int UninitializedAzimuth = -1;
const unsigned RenderingQuantum = 128;
const unsigned RenderingQuantum = WEBAUDIO_BLOCK_SIZE;
HRTFPanner::HRTFPanner(float sampleRate, mozilla::TemporaryRef<HRTFDatabaseLoader> databaseLoader)
: m_databaseLoader(databaseLoader)
@ -55,10 +54,7 @@ HRTFPanner::HRTFPanner(float sampleRate, mozilla::TemporaryRef<HRTFDatabaseLoade
, m_convolverR1(m_convolverL1.fftSize())
, m_convolverL2(m_convolverL1.fftSize())
, m_convolverR2(m_convolverL1.fftSize())
, m_delayLineL(ceilf(MaxDelayTimeSeconds * sampleRate),
WebAudioUtils::ComputeSmoothingRate(0.02, sampleRate))
, m_delayLineR(ceilf(MaxDelayTimeSeconds * sampleRate),
WebAudioUtils::ComputeSmoothingRate(0.02, sampleRate))
, m_delayLine(ceilf(MaxDelayTimeSeconds * sampleRate), 1.0)
{
MOZ_ASSERT(m_databaseLoader);
MOZ_COUNT_CTOR(HRTFPanner);
@ -86,8 +82,7 @@ void HRTFPanner::reset()
m_convolverR1.reset();
m_convolverL2.reset();
m_convolverR2.reset();
m_delayLineL.Reset();
m_delayLineR.Reset();
m_delayLine.Reset();
}
int HRTFPanner::calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend)
@ -115,15 +110,15 @@ int HRTFPanner::calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azi
return desiredAzimuthIndex;
}
void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioChunk* inputBus, AudioChunk* outputBus, TrackTicks framesToProcess)
void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioChunk* inputBus, AudioChunk* outputBus)
{
unsigned numInputChannels =
inputBus->IsNull() ? 0 : inputBus->mChannelData.Length();
MOZ_ASSERT(numInputChannels <= 2);
MOZ_ASSERT(framesToProcess <= inputBus->mDuration);
MOZ_ASSERT(inputBus->mDuration == WEBAUDIO_BLOCK_SIZE);
bool isOutputGood = outputBus && outputBus->mChannelData.Length() == 2 && framesToProcess <= outputBus->mDuration;
bool isOutputGood = outputBus && outputBus->mChannelData.Length() == 2 && outputBus->mDuration == WEBAUDIO_BLOCK_SIZE;
MOZ_ASSERT(isOutputGood);
if (!isOutputGood) {
@ -151,11 +146,7 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioChunk*
// Normally, we'll just be dealing with mono sources.
// If we have a stereo input, implement stereo panning with left source processed by left HRTF, and right source by right HRTF.
// Get source and destination pointers.
const float* sourceL = numInputChannels > 0 ?
static_cast<const float*>(inputBus->mChannelData[0]) : nullptr;
const float* sourceR = numInputChannels > 1 ?
static_cast<const float*>(inputBus->mChannelData[1]) : sourceL;
// Get destination pointers.
float* destinationL =
static_cast<float*>(const_cast<void*>(outputBus->mChannelData[0]));
float* destinationR =
@ -197,95 +188,94 @@ void HRTFPanner::pan(double desiredAzimuth, double elevation, const AudioChunk*
}
}
// This algorithm currently requires that we process in power-of-two size chunks at least RenderingQuantum.
MOZ_ASSERT(framesToProcess && 0 == (framesToProcess & (framesToProcess - 1)));
MOZ_ASSERT(framesToProcess >= RenderingQuantum);
// Get the HRTFKernels and interpolated delays.
HRTFKernel* kernelL1;
HRTFKernel* kernelR1;
HRTFKernel* kernelL2;
HRTFKernel* kernelR2;
double frameDelayL1;
double frameDelayR1;
double frameDelayL2;
double frameDelayR2;
database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex1, m_elevation1, kernelL1, kernelR1, frameDelayL1, frameDelayR1);
database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex2, m_elevation2, kernelL2, kernelR2, frameDelayL2, frameDelayR2);
const unsigned framesPerSegment = RenderingQuantum;
const unsigned numberOfSegments = framesToProcess / framesPerSegment;
bool areKernelsGood = kernelL1 && kernelR1 && kernelL2 && kernelR2;
MOZ_ASSERT(areKernelsGood);
if (!areKernelsGood) {
outputBus->SetNull(outputBus->mDuration);
return;
}
for (unsigned segment = 0; segment < numberOfSegments; ++segment) {
// Get the HRTFKernels and interpolated delays.
HRTFKernel* kernelL1;
HRTFKernel* kernelR1;
HRTFKernel* kernelL2;
HRTFKernel* kernelR2;
double frameDelayL1;
double frameDelayR1;
double frameDelayL2;
double frameDelayR2;
database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex1, m_elevation1, kernelL1, kernelR1, frameDelayL1, frameDelayR1);
database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex2, m_elevation2, kernelL2, kernelR2, frameDelayL2, frameDelayR2);
MOZ_ASSERT(frameDelayL1 / sampleRate() < MaxDelayTimeSeconds && frameDelayR1 / sampleRate() < MaxDelayTimeSeconds);
MOZ_ASSERT(frameDelayL2 / sampleRate() < MaxDelayTimeSeconds && frameDelayR2 / sampleRate() < MaxDelayTimeSeconds);
bool areKernelsGood = kernelL1 && kernelR1 && kernelL2 && kernelR2;
MOZ_ASSERT(areKernelsGood);
if (!areKernelsGood) {
outputBus->SetNull(outputBus->mDuration);
return;
// Crossfade inter-aural delays based on transitions.
double frameDelaysL[WEBAUDIO_BLOCK_SIZE];
double frameDelaysR[WEBAUDIO_BLOCK_SIZE];
{
float x = m_crossfadeX;
float incr = m_crossfadeIncr;
for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
frameDelaysL[i] = (1 - x) * frameDelayL1 + x * frameDelayL2;
frameDelaysR[i] = (1 - x) * frameDelayR1 + x * frameDelayR2;
x += incr;
}
}
// First run through delay lines for inter-aural time difference.
m_delayLine.Write(*inputBus);
// "Speakers" means a mono input is read into both outputs (with possibly
// different delays).
m_delayLine.ReadChannel(frameDelaysL, outputBus, 0,
ChannelInterpretation::Speakers);
m_delayLine.ReadChannel(frameDelaysR, outputBus, 1,
ChannelInterpretation::Speakers);
m_delayLine.NextBlock();
bool needsCrossfading = m_crossfadeIncr;
// Have the convolvers render directly to the final destination if we're not cross-fading.
float* convolutionDestinationL1 = needsCrossfading ? m_tempL1.Elements() : destinationL;
float* convolutionDestinationR1 = needsCrossfading ? m_tempR1.Elements() : destinationR;
float* convolutionDestinationL2 = needsCrossfading ? m_tempL2.Elements() : destinationL;
float* convolutionDestinationR2 = needsCrossfading ? m_tempR2.Elements() : destinationR;
// Now do the convolutions.
// Note that we avoid doing convolutions on both sets of convolvers if we're not currently cross-fading.
if (m_crossfadeSelection == CrossfadeSelection1 || needsCrossfading) {
m_convolverL1.process(kernelL1->fftFrame(), destinationL, convolutionDestinationL1, WEBAUDIO_BLOCK_SIZE);
m_convolverR1.process(kernelR1->fftFrame(), destinationR, convolutionDestinationR1, WEBAUDIO_BLOCK_SIZE);
}
if (m_crossfadeSelection == CrossfadeSelection2 || needsCrossfading) {
m_convolverL2.process(kernelL2->fftFrame(), destinationL, convolutionDestinationL2, WEBAUDIO_BLOCK_SIZE);
m_convolverR2.process(kernelR2->fftFrame(), destinationR, convolutionDestinationR2, WEBAUDIO_BLOCK_SIZE);
}
if (needsCrossfading) {
// Apply linear cross-fade.
float x = m_crossfadeX;
float incr = m_crossfadeIncr;
for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
destinationL[i] = (1 - x) * convolutionDestinationL1[i] + x * convolutionDestinationL2[i];
destinationR[i] = (1 - x) * convolutionDestinationR1[i] + x * convolutionDestinationR2[i];
x += incr;
}
// Update cross-fade value from local.
m_crossfadeX = x;
MOZ_ASSERT(frameDelayL1 / sampleRate() < MaxDelayTimeSeconds && frameDelayR1 / sampleRate() < MaxDelayTimeSeconds);
MOZ_ASSERT(frameDelayL2 / sampleRate() < MaxDelayTimeSeconds && frameDelayR2 / sampleRate() < MaxDelayTimeSeconds);
// Crossfade inter-aural delays based on transitions.
double frameDelayL = (1 - m_crossfadeX) * frameDelayL1 + m_crossfadeX * frameDelayL2;
double frameDelayR = (1 - m_crossfadeX) * frameDelayR1 + m_crossfadeX * frameDelayR2;
// Calculate the source and destination pointers for the current segment.
unsigned offset = segment * framesPerSegment;
const float* segmentSourceL = sourceL ? sourceL + offset : nullptr;
const float* segmentSourceR = sourceR ? sourceR + offset : nullptr;
float* segmentDestinationL = destinationL + offset;
float* segmentDestinationR = destinationR + offset;
// First run through delay lines for inter-aural time difference.
m_delayLineL.Process(frameDelayL, &segmentSourceL, &segmentDestinationL, 1, framesPerSegment);
m_delayLineR.Process(frameDelayR, &segmentSourceR, &segmentDestinationR, 1, framesPerSegment);
bool needsCrossfading = m_crossfadeIncr;
// Have the convolvers render directly to the final destination if we're not cross-fading.
float* convolutionDestinationL1 = needsCrossfading ? m_tempL1.Elements() : segmentDestinationL;
float* convolutionDestinationR1 = needsCrossfading ? m_tempR1.Elements() : segmentDestinationR;
float* convolutionDestinationL2 = needsCrossfading ? m_tempL2.Elements() : segmentDestinationL;
float* convolutionDestinationR2 = needsCrossfading ? m_tempR2.Elements() : segmentDestinationR;
// Now do the convolutions.
// Note that we avoid doing convolutions on both sets of convolvers if we're not currently cross-fading.
if (m_crossfadeSelection == CrossfadeSelection1 || needsCrossfading) {
m_convolverL1.process(kernelL1->fftFrame(), segmentDestinationL, convolutionDestinationL1, framesPerSegment);
m_convolverR1.process(kernelR1->fftFrame(), segmentDestinationR, convolutionDestinationR1, framesPerSegment);
}
if (m_crossfadeSelection == CrossfadeSelection2 || needsCrossfading) {
m_convolverL2.process(kernelL2->fftFrame(), segmentDestinationL, convolutionDestinationL2, framesPerSegment);
m_convolverR2.process(kernelR2->fftFrame(), segmentDestinationR, convolutionDestinationR2, framesPerSegment);
}
if (needsCrossfading) {
// Apply linear cross-fade.
float x = m_crossfadeX;
float incr = m_crossfadeIncr;
for (unsigned i = 0; i < framesPerSegment; ++i) {
segmentDestinationL[i] = (1 - x) * convolutionDestinationL1[i] + x * convolutionDestinationL2[i];
segmentDestinationR[i] = (1 - x) * convolutionDestinationR1[i] + x * convolutionDestinationR2[i];
x += incr;
}
// Update cross-fade value from local.
m_crossfadeX = x;
if (m_crossfadeIncr > 0 && fabs(m_crossfadeX - 1) < m_crossfadeIncr) {
// We've fully made the crossfade transition from 1 -> 2.
m_crossfadeSelection = CrossfadeSelection2;
m_crossfadeX = 1;
m_crossfadeIncr = 0;
} else if (m_crossfadeIncr < 0 && fabs(m_crossfadeX) < -m_crossfadeIncr) {
// We've fully made the crossfade transition from 2 -> 1.
m_crossfadeSelection = CrossfadeSelection1;
m_crossfadeX = 0;
m_crossfadeIncr = 0;
}
if (m_crossfadeIncr > 0 && fabs(m_crossfadeX - 1) < m_crossfadeIncr) {
// We've fully made the crossfade transition from 1 -> 2.
m_crossfadeSelection = CrossfadeSelection2;
m_crossfadeX = 1;
m_crossfadeIncr = 0;
} else if (m_crossfadeIncr < 0 && fabs(m_crossfadeX) < -m_crossfadeIncr) {
// We've fully made the crossfade transition from 2 -> 1.
m_crossfadeSelection = CrossfadeSelection1;
m_crossfadeX = 0;
m_crossfadeIncr = 0;
}
}
}
@ -299,7 +289,7 @@ int HRTFPanner::maxTailFrames() const
// tailTime of the DelayKernel and the tailTime of the FFTConvolver.
// The FFTConvolver has a tail time of fftSize(), including latency of
// fftSize()/2.
return m_delayLineL.MaxDelayFrames() + fftSize();
return m_delayLine.MaxDelayTicks() + fftSize();
}
} // namespace WebCore

View File

@ -26,7 +26,7 @@
#define HRTFPanner_h
#include "FFTConvolver.h"
#include "DelayProcessor.h"
#include "DelayBuffer.h"
namespace mozilla {
struct AudioChunk;
@ -43,8 +43,8 @@ public:
HRTFPanner(float sampleRate, mozilla::TemporaryRef<HRTFDatabaseLoader> databaseLoader);
~HRTFPanner();
// framesToProcess must be a power of 2 and greater than 128
void pan(double azimuth, double elevation, const AudioChunk* inputBus, AudioChunk* outputBus, mozilla::TrackTicks framesToProcess);
// chunk durations must be 128
void pan(double azimuth, double elevation, const AudioChunk* inputBus, AudioChunk* outputBus);
void reset();
size_t fftSize() const { return m_convolverL1.fftSize(); }
@ -99,8 +99,7 @@ private:
FFTConvolver m_convolverL2;
FFTConvolver m_convolverR2;
mozilla::DelayProcessor m_delayLineL;
mozilla::DelayProcessor m_delayLineR;
mozilla::DelayBuffer m_delayLine;
AudioFloatArray m_tempL1;
AudioFloatArray m_tempR1;

View File

@ -62,8 +62,8 @@ UNIFIED_SOURCES += [
'ChannelMergerNode.cpp',
'ChannelSplitterNode.cpp',
'ConvolverNode.cpp',
'DelayBuffer.cpp',
'DelayNode.cpp',
'DelayProcessor.cpp',
'DynamicsCompressorNode.cpp',
'FFTBlock.cpp',
'GainNode.cpp',

View File

@ -79,6 +79,7 @@ support-files =
[test_decodeMultichannel.html]
[test_delayNode.html]
[test_delayNodeAtMax.html]
[test_delayNodeChannelChanges.html]
[test_delayNodeCycles.html]
[test_delayNodeSmallMaxDelay.html]
[test_delayNodeTailIncrease.html]

View File

@ -0,0 +1,97 @@
<!DOCTYPE HTML>
<html>
<head>
<title>test DelayNode channel count changes</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
const bufferSize = 4096;
var ctx;
var testDelay;
var stereoDelay;
var invertor;
function compareOutputs(callback) {
var processor = ctx.createScriptProcessor(bufferSize, 2, 0);
testDelay.connect(processor);
invertor.connect(processor);
processor.onaudioprocess =
function(e) {
compareBuffers(e.inputBuffer,
ctx.createBuffer(2, bufferSize, ctx.sampleRate));
e.target.onaudioprocess = null;
callback();
}
}
function startTest() {
// And a two-channel signal
var merger = ctx.createChannelMerger();
merger.connect(testDelay);
merger.connect(stereoDelay);
var oscL = ctx.createOscillator();
oscL.connect(merger, 0, 0);
oscL.start(0);
var oscR = ctx.createOscillator();
oscR.type = "sawtooth";
oscR.connect(merger, 0, 1);
oscR.start(0);
compareOutputs(
function () {
// Disconnect the two-channel signal and test again
merger.disconnect();
compareOutputs(SimpleTest.finish);
});
}
function prepareTest() {
ctx = new AudioContext();
// The output of a test delay node with mono and stereo input will be
// compared with that of separate mono and stereo delay nodes.
const delayTime = 0.3 * bufferSize / ctx.sampleRate;
testDelay = ctx.createDelay(delayTime);
testDelay.delayTime.value = delayTime;
monoDelay = ctx.createDelay(delayTime);
monoDelay.delayTime.value = delayTime;
stereoDelay = ctx.createDelay(delayTime);
stereoDelay.delayTime.value = delayTime;
// Create a one-channel signal and connect to the delay nodes
var monoOsc = ctx.createOscillator();
monoOsc.frequency.value = 110;
monoOsc.connect(testDelay);
monoOsc.connect(monoDelay);
monoOsc.start(0);
// Invert the expected so that mixing with the test will find the difference.
invertor = ctx.createGain();
invertor.gain.value = -1.0;
monoDelay.connect(invertor);
stereoDelay.connect(invertor);
// Start the test after the delay nodes have begun processing.
var processor = ctx.createScriptProcessor(bufferSize, 1, 0);
processor.connect(ctx.destination);
processor.onaudioprocess =
function(e) {
e.target.onaudioprocess = null;
processor.disconnect();
startTest();
};
}
prepareTest();
</script>
</pre>
</body>
</html>

View File

@ -12,6 +12,7 @@
#include "nsError.h"
#include "nsSVGAnimatedTransformList.h"
#include "nsSVGAttrTearoffTable.h"
#include "mozilla/DebugOnly.h"
namespace {
const double kRadPerDegree = 2.0 * M_PI / 360.0;

View File

@ -84,7 +84,7 @@ nsXMLElement::NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
const nsAttrValue* attrVal =
mAttrsAndChildren.GetAttr(aOldNodeInfo->GetIDAttributeAtom());
if (attrVal) {
doc->RemoveFromIdTable(this, attrVal->GetAtomValue());
RemoveFromIdTable(attrVal->GetAtomValue());
}
}
@ -104,7 +104,7 @@ nsXMLElement::NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
NS_ASSERTION(attrVal->Type() == nsAttrValue::eAtom,
"Should be atom by now");
if (doc) {
doc->AddToIdTable(this, attrVal->GetAtomValue());
AddToIdTable(attrVal->GetAtomValue());
}
}
}
@ -133,28 +133,3 @@ nsXMLElement::ParseAttribute(int32_t aNamespaceID,
return false;
}
nsresult
nsXMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers)
{
nsresult rv = Element::BindToTree(aDocument, aParent,
aBindingParent,
aCompileEventHandlers);
NS_ENSURE_SUCCESS(rv, rv);
if (aDocument && HasID() && !GetBindingParent()) {
aDocument->AddToIdTable(this, DoGetID());
}
return NS_OK;
}
void
nsXMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
RemoveFromIdTable();
return Element::UnbindFromTree(aDeep, aNullParent);
}

View File

@ -37,10 +37,6 @@ public:
// nsIContent interface methods
virtual nsIAtom *GetIDAttributeName() const MOZ_OVERRIDE;
virtual nsIAtom* DoGetID() const MOZ_OVERRIDE;
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers) MOZ_OVERRIDE;
virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute,
bool aNotify) MOZ_OVERRIDE;
virtual bool ParseAttribute(int32_t aNamespaceID,

View File

@ -58,7 +58,7 @@
#include "nsIXULTemplateBuilder.h"
#include "nsLayoutCID.h"
#include "nsContentCID.h"
#include "nsDOMEvent.h"
#include "mozilla/dom/Event.h"
#include "nsRDFCID.h"
#include "nsStyleConsts.h"
#include "nsXPIDLString.h"
@ -1185,7 +1185,7 @@ nsXULElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
// handling.
nsCOMPtr<nsIDOMEvent> domEvent = aVisitor.mDOMEvent;
while (domEvent) {
nsDOMEvent* event = domEvent->InternalDOMEvent();
Event* event = domEvent->InternalDOMEvent();
NS_ENSURE_STATE(!SameCOMIdentity(event->GetOriginalTarget(),
commandContent));
nsCOMPtr<nsIDOMXULCommandEvent> commandEvent =

View File

@ -32,6 +32,7 @@
#include "nsHTMLReflowState.h"
#include "nsIObjectLoadingContent.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/FragmentOrElement.h"
@ -43,7 +44,6 @@
#include "nsViewManager.h"
#include "nsError.h"
#include "nsMenuFrame.h"
#include "nsDOMEvent.h"
using namespace mozilla;
using namespace mozilla::dom;

View File

@ -1720,6 +1720,7 @@ XULDocument::AddElementToDocumentPre(Element* aElement)
// elements from prototypes.
nsIAtom* id = aElement->GetID();
if (id) {
// FIXME: Shouldn't BindToTree take care of this?
nsAutoScriptBlocker scriptBlocker;
AddToIdTable(aElement, id);
}
@ -1853,6 +1854,7 @@ XULDocument::RemoveSubtreeFromDocument(nsIContent* aContent)
RemoveElementFromRefMap(aElement);
nsIAtom* id = aElement->GetID();
if (id) {
// FIXME: Shouldn't UnbindFromTree take care of this?
nsAutoScriptBlocker scriptBlocker;
RemoveFromIdTable(aElement, id);
}

View File

@ -1090,6 +1090,10 @@ Console::ProcessArguments(JSContext* aCx,
}
++start;
if (start == end) {
output.Append('%');
break;
}
if (*start == '%') {
output.Append(*start);
@ -1111,13 +1115,23 @@ Console::ProcessArguments(JSContext* aCx,
integer = integer * 10 + *start - '0';
tmp.Append(*start);
++start;
} while (*start >= '0' && *start <= '9');
} while (*start >= '0' && *start <= '9' && start != end);
}
if (start == end) {
output.Append(tmp);
break;
}
if (*start == '.') {
tmp.Append(*start);
++start;
if (start == end) {
output.Append(tmp);
break;
}
// '.' must be followed by a number.
if (*start < '0' || *start > '9') {
output.Append(tmp);
@ -1130,7 +1144,12 @@ Console::ProcessArguments(JSContext* aCx,
mantissa = mantissa * 10 + *start - '0';
tmp.Append(*start);
++start;
} while (*start >= '0' && *start <= '9');
} while (*start >= '0' && *start <= '9' && start != end);
if (start == end) {
output.Append(tmp);
break;
}
}
char ch = *start;

View File

@ -4,6 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MessagePort.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/MessageChannel.h"
#include "mozilla/dom/MessagePortBinding.h"
#include "mozilla/dom/StructuredCloneTags.h"
@ -11,7 +12,6 @@
#include "nsContentUtils.h"
#include "nsEventDispatcher.h"
#include "nsPresContext.h"
#include "nsDOMEvent.h"
#include "nsIDocument.h"
#include "nsIDOMFile.h"
@ -55,24 +55,16 @@ class PostMessageRunnable : public nsRunnable
NS_DECL_NSIRUNNABLE
PostMessageRunnable()
: mMessage(nullptr)
, mMessageLen(0)
{
}
~PostMessageRunnable()
{
// Ensure that the buffer is freed
if (mMessage) {
JSAutoStructuredCloneBuffer buffer;
buffer.adopt(mMessage, mMessageLen);
}
}
void SetJSData(JSAutoStructuredCloneBuffer& aBuffer)
JSAutoStructuredCloneBuffer& Buffer()
{
NS_ASSERTION(!mMessage && mMessageLen == 0, "Don't call twice!");
aBuffer.steal(&mMessage, &mMessageLen);
return mBuffer;
}
bool StoreISupports(nsISupports* aSupports)
@ -89,8 +81,7 @@ class PostMessageRunnable : public nsRunnable
private:
nsRefPtr<MessagePort> mPort;
uint64_t* mMessage;
size_t mMessageLen;
JSAutoStructuredCloneBuffer mBuffer;
nsTArray<nsCOMPtr<nsISupports> > mSupportsArray;
};
@ -225,12 +216,6 @@ PostMessageRunnable::Run()
{
MOZ_ASSERT(mPort);
// Ensure that the buffer is freed even if we fail to post the message
JSAutoStructuredCloneBuffer buffer;
buffer.adopt(mMessage, mMessageLen);
mMessage = nullptr;
mMessageLen = 0;
// Get the JSContext for the target window
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(mPort->GetOwner());
NS_ENSURE_STATE(sgo);
@ -247,7 +232,7 @@ PostMessageRunnable::Run()
scInfo.mEvent = this;
scInfo.mPort = mPort;
if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
if (!mBuffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) {
return NS_ERROR_DOM_DATA_CLONE_ERR;
}
}
@ -259,7 +244,7 @@ PostMessageRunnable::Run()
}
ErrorResult error;
nsRefPtr<nsDOMEvent> event =
nsRefPtr<Event> event =
doc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), error);
if (error.Failed()) {
return NS_OK;
@ -364,7 +349,6 @@ MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
// We *must* clone the data here, or the JS::Value could be modified
// by script
JSAutoStructuredCloneBuffer buffer;
StructuredCloneInfo scInfo;
scInfo.mEvent = event;
scInfo.mPort = this;
@ -388,14 +372,12 @@ MessagePort::PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
transferable.setObject(*array);
}
if (!buffer.write(aCx, aMessage, transferable, &kPostMessageCallbacks,
&scInfo)) {
if (!event->Buffer().write(aCx, aMessage, transferable,
&kPostMessageCallbacks, &scInfo)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
event->SetJSData(buffer);
if (!mEntangledPort) {
return;
}

View File

@ -35,7 +35,7 @@
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "Connection.h"
#include "nsDOMEvent.h"
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
#include "nsGlobalWindow.h"
#ifdef MOZ_B2G_RIL
#include "mozilla/dom/IccManager.h"

View File

@ -54,18 +54,18 @@ DOMCI_CASTABLE_NODECL_INTERFACE(mozilla::dom::Element, mozilla::dom::Element,\
/* If this is ever removed, the IID for EventTarget can go away */ \
DOMCI_CASTABLE_NODECL_INTERFACE(mozilla::dom::EventTarget, \
mozilla::dom::EventTarget, 2, _extra) \
DOMCI_CASTABLE_INTERFACE(nsDOMEvent, nsIDOMEvent, 3, _extra) \
DOMCI_CASTABLE_NODECL_INTERFACE(mozilla::dom::Event, nsIDOMEvent, 3, _extra) \
DOMCI_CASTABLE_INTERFACE(nsIDocument, nsIDocument, 4, _extra) \
DOMCI_CASTABLE_INTERFACE(nsDocument, nsIDocument, 5, _extra) \
DOMCI_CASTABLE_INTERFACE(nsGenericHTMLElement, nsIContent, 6, _extra) \
DOMCI_CASTABLE_INTERFACE(nsHTMLDocument, nsIDocument, 7, _extra) \
DOMCI_CASTABLE_INTERFACE(nsStyledElement, nsStyledElement, 8, _extra) \
DOMCI_CASTABLE_INTERFACE(nsSVGElement, nsIContent, 9, _extra) \
/* NOTE: When removing the casts below, remove the nsDOMEventBase class */ \
/* NOTE: When removing the casts below, remove the dom::EventBase class */ \
DOMCI_CASTABLE_NODECL_INTERFACE(mozilla::dom::MouseEvent, \
nsDOMEventBase, 10, _extra) \
mozilla::dom::EventBase, 10, _extra) \
DOMCI_CASTABLE_NODECL_INTERFACE(mozilla::dom::UIEvent, \
nsDOMEventBase, 11, _extra) \
mozilla::dom::EventBase, 11, _extra) \
DOMCI_CASTABLE_INTERFACE(nsGlobalWindow, nsIDOMEventTarget, 12, _extra)
// Make sure all classes mentioned in DOMCI_CASTABLE_INTERFACES
@ -78,6 +78,7 @@ DOMCI_CASTABLE_INTERFACES(unused)
namespace mozilla {
namespace dom {
class Element;
class Event;
class EventTarget;
class MouseEvent;
class UIEvent;

Some files were not shown because too many files have changed in this diff Show More