mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b-i
This commit is contained in:
commit
9b61e0092a
@ -300,47 +300,114 @@ nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult)
|
||||
|
||||
*aResult = false;
|
||||
|
||||
int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
HyperTextAccessible* text = mPosition->AsHyperText();
|
||||
Accessible* oldPosition = mPosition;
|
||||
while (!text) {
|
||||
oldPosition = mPosition;
|
||||
mPosition = mPosition->Parent();
|
||||
text = mPosition->AsHyperText();
|
||||
}
|
||||
int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
|
||||
Accessible* tempPosition = mPosition;
|
||||
Accessible* root = GetActiveRoot();
|
||||
while (true) {
|
||||
Accessible* curPosition = tempPosition;
|
||||
HyperTextAccessible* text;
|
||||
// Find the nearest text node using a preorder traversal starting from
|
||||
// the current node.
|
||||
if (!(text = tempPosition->AsHyperText())) {
|
||||
text = SearchForText(tempPosition, false);
|
||||
if (!text)
|
||||
return NS_OK;
|
||||
if (text != curPosition)
|
||||
tempStart = tempEnd = -1;
|
||||
tempPosition = text;
|
||||
}
|
||||
|
||||
if (mEndOffset == -1)
|
||||
mEndOffset = text != oldPosition ? text->GetChildOffset(oldPosition) : 0;
|
||||
// If the search led to the parent of the node we started on (e.g. when
|
||||
// starting on a text leaf), start the text movement from the end of that
|
||||
// node, otherwise we just default to 0.
|
||||
if (tempEnd == -1)
|
||||
tempEnd = text == curPosition->Parent() ?
|
||||
text->GetChildOffset(curPosition) : 0;
|
||||
|
||||
if (mEndOffset == text->CharacterCount())
|
||||
// If there's no more text on the current node, try to find the next text
|
||||
// node; if there isn't one, bail out.
|
||||
if (tempEnd == text->CharacterCount()) {
|
||||
if (tempPosition == root)
|
||||
return NS_OK;
|
||||
|
||||
// If we're currently sitting on a link, try move to either the next
|
||||
// sibling or the parent, whichever is closer to the current end
|
||||
// offset. Otherwise, do a forward search for the next node to land on
|
||||
// (we don't do this in the first case because we don't want to go to the
|
||||
// subtree).
|
||||
Accessible* sibling = tempPosition->NextSibling();
|
||||
if (tempPosition->IsLink()) {
|
||||
if (sibling && sibling->IsLink()) {
|
||||
tempStart = tempEnd = -1;
|
||||
tempPosition = sibling;
|
||||
} else {
|
||||
tempStart = tempPosition->StartOffset();
|
||||
tempEnd = tempPosition->EndOffset();
|
||||
tempPosition = tempPosition->Parent();
|
||||
}
|
||||
} else {
|
||||
tempPosition = SearchForText(tempPosition, false);
|
||||
if (!tempPosition)
|
||||
return NS_OK;
|
||||
tempStart = tempEnd = -1;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
AccessibleTextBoundary startBoundary, endBoundary;
|
||||
switch (aBoundary) {
|
||||
case CHAR_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
break;
|
||||
case WORD_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoString unusedText;
|
||||
int32_t newStart = 0, newEnd = 0, currentEnd = tempEnd;
|
||||
text->GetTextAtOffset(tempEnd, endBoundary, &newStart, &tempEnd, unusedText);
|
||||
text->GetTextBeforeOffset(tempEnd, startBoundary, &newStart, &newEnd,
|
||||
unusedText);
|
||||
int32_t potentialStart = newEnd == tempEnd ? newStart : newEnd;
|
||||
tempStart = potentialStart > tempStart ? potentialStart : currentEnd;
|
||||
|
||||
// The offset range we've obtained might have embedded characters in it,
|
||||
// limit the range to the start of the first occurrence of an embedded
|
||||
// character.
|
||||
Accessible* childAtOffset = nullptr;
|
||||
for (int32_t i = tempStart; i < tempEnd; i++) {
|
||||
childAtOffset = text->GetChildAtOffset(i);
|
||||
if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
|
||||
tempEnd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there's an embedded character at the very start of the range, we
|
||||
// instead want to traverse into it. So restart the movement with
|
||||
// the child as the starting point.
|
||||
if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
|
||||
tempStart == childAtOffset->StartOffset()) {
|
||||
tempPosition = childAtOffset;
|
||||
tempStart = tempEnd = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
*aResult = true;
|
||||
|
||||
Accessible* startPosition = mPosition;
|
||||
int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
mPosition = tempPosition;
|
||||
mStartOffset = tempStart;
|
||||
mEndOffset = tempEnd;
|
||||
NotifyOfPivotChange(startPosition, oldStart, oldEnd,
|
||||
nsIAccessiblePivot::REASON_TEXT);
|
||||
return NS_OK;
|
||||
|
||||
AccessibleTextBoundary startBoundary, endBoundary;
|
||||
switch (aBoundary) {
|
||||
case CHAR_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
break;
|
||||
case WORD_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoString unusedText;
|
||||
int32_t newStart = 0, newEnd = 0;
|
||||
text->GetTextAtOffset(mEndOffset, endBoundary, &newStart, &mEndOffset, unusedText);
|
||||
text->GetTextBeforeOffset(mEndOffset, startBoundary, &newStart, &newEnd,
|
||||
unusedText);
|
||||
mStartOffset = newEnd == mEndOffset ? newStart : newEnd;
|
||||
|
||||
*aResult = true;
|
||||
|
||||
NotifyOfPivotChange(mPosition, oldStart, oldEnd,
|
||||
nsIAccessiblePivot::REASON_TEXT);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -350,52 +417,127 @@ nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult)
|
||||
|
||||
*aResult = false;
|
||||
|
||||
int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
HyperTextAccessible* text = mPosition->AsHyperText();
|
||||
Accessible* oldPosition = mPosition;
|
||||
while (!text) {
|
||||
oldPosition = mPosition;
|
||||
mPosition = mPosition->Parent();
|
||||
text = mPosition->AsHyperText();
|
||||
}
|
||||
int32_t tempStart = mStartOffset, tempEnd = mEndOffset;
|
||||
Accessible* tempPosition = mPosition;
|
||||
Accessible* root = GetActiveRoot();
|
||||
while (true) {
|
||||
Accessible* curPosition = tempPosition;
|
||||
HyperTextAccessible* text;
|
||||
// Find the nearest text node using a reverse preorder traversal starting
|
||||
// from the current node.
|
||||
if (!(text = tempPosition->AsHyperText())) {
|
||||
text = SearchForText(tempPosition, true);
|
||||
if (!text)
|
||||
return NS_OK;
|
||||
if (text != curPosition)
|
||||
tempStart = tempEnd = -1;
|
||||
tempPosition = text;
|
||||
}
|
||||
|
||||
if (mStartOffset == -1)
|
||||
mStartOffset = text != oldPosition ? text->GetChildOffset(oldPosition) : 0;
|
||||
// If the search led to the parent of the node we started on (e.g. when
|
||||
// starting on a text leaf), start the text movement from the end of that
|
||||
// node, otherwise we just default to 0.
|
||||
if (tempStart == -1) {
|
||||
if (tempPosition != curPosition)
|
||||
tempStart = text == curPosition->Parent() ?
|
||||
text->GetChildOffset(curPosition) : text->CharacterCount();
|
||||
else
|
||||
tempStart = 0;
|
||||
}
|
||||
|
||||
if (mStartOffset == 0)
|
||||
// If there's no more text on the current node, try to find the previous
|
||||
// text node; if there isn't one, bail out.
|
||||
if (tempStart == 0) {
|
||||
if (tempPosition == root)
|
||||
return NS_OK;
|
||||
|
||||
// If we're currently sitting on a link, try move to either the previous
|
||||
// sibling or the parent, whichever is closer to the current end
|
||||
// offset. Otherwise, do a forward search for the next node to land on
|
||||
// (we don't do this in the first case because we don't want to go to the
|
||||
// subtree).
|
||||
Accessible* sibling = tempPosition->PrevSibling();
|
||||
if (tempPosition->IsLink()) {
|
||||
if (sibling && sibling->IsLink()) {
|
||||
HyperTextAccessible* siblingText = sibling->AsHyperText();
|
||||
tempStart = tempEnd = siblingText ?
|
||||
siblingText->CharacterCount() : -1;
|
||||
tempPosition = sibling;
|
||||
} else {
|
||||
tempStart = tempPosition->StartOffset();
|
||||
tempEnd = tempPosition->EndOffset();
|
||||
tempPosition = tempPosition->Parent();
|
||||
}
|
||||
} else {
|
||||
HyperTextAccessible* tempText = SearchForText(tempPosition, true);
|
||||
if (!tempText)
|
||||
return NS_OK;
|
||||
tempPosition = tempText;
|
||||
tempStart = tempEnd = tempText->CharacterCount();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
AccessibleTextBoundary startBoundary, endBoundary;
|
||||
switch (aBoundary) {
|
||||
case CHAR_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
break;
|
||||
case WORD_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoString unusedText;
|
||||
int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0;
|
||||
text->GetTextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd,
|
||||
unusedText);
|
||||
if (newStart < tempStart)
|
||||
tempStart = newEnd >= currentStart ? newStart : newEnd;
|
||||
else // XXX: In certain odd cases newStart is equal to tempStart
|
||||
text->GetTextBeforeOffset(tempStart - 1, startBoundary, &newStart,
|
||||
&tempStart, unusedText);
|
||||
text->GetTextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd,
|
||||
unusedText);
|
||||
tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart;
|
||||
|
||||
// The offset range we've obtained might have embedded characters in it,
|
||||
// limit the range to the start of the last occurrence of an embedded
|
||||
// character.
|
||||
Accessible* childAtOffset = nullptr;
|
||||
for (int32_t i = tempEnd - 1; i >= tempStart; i--) {
|
||||
childAtOffset = text->GetChildAtOffset(i);
|
||||
if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) {
|
||||
tempStart = childAtOffset->EndOffset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there's an embedded character at the very end of the range, we
|
||||
// instead want to traverse into it. So restart the movement with
|
||||
// the child as the starting point.
|
||||
if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) &&
|
||||
tempEnd == childAtOffset->EndOffset()) {
|
||||
tempPosition = childAtOffset;
|
||||
tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount();
|
||||
continue;
|
||||
}
|
||||
|
||||
*aResult = true;
|
||||
|
||||
Accessible* startPosition = mPosition;
|
||||
int32_t oldStart = mStartOffset, oldEnd = mEndOffset;
|
||||
mPosition = tempPosition;
|
||||
mStartOffset = tempStart;
|
||||
mEndOffset = tempEnd;
|
||||
|
||||
NotifyOfPivotChange(startPosition, oldStart, oldEnd,
|
||||
nsIAccessiblePivot::REASON_TEXT);
|
||||
return NS_OK;
|
||||
|
||||
AccessibleTextBoundary startBoundary, endBoundary;
|
||||
switch (aBoundary) {
|
||||
case CHAR_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_CHAR;
|
||||
break;
|
||||
case WORD_BOUNDARY:
|
||||
startBoundary = nsIAccessibleText::BOUNDARY_WORD_START;
|
||||
endBoundary = nsIAccessibleText::BOUNDARY_WORD_END;
|
||||
break;
|
||||
default:
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsAutoString unusedText;
|
||||
int32_t newStart = 0, newEnd = 0;
|
||||
text->GetTextBeforeOffset(mStartOffset, startBoundary, &newStart, &newEnd,
|
||||
unusedText);
|
||||
if (newStart < mStartOffset)
|
||||
mStartOffset = newEnd == mStartOffset ? newStart : newEnd;
|
||||
else // XXX: In certain odd cases newStart is equal to mStartOffset
|
||||
text->GetTextBeforeOffset(mStartOffset - 1, startBoundary, &newStart,
|
||||
&mStartOffset, unusedText);
|
||||
text->GetTextAtOffset(mStartOffset, endBoundary, &newStart, &mEndOffset,
|
||||
unusedText);
|
||||
|
||||
*aResult = true;
|
||||
|
||||
NotifyOfPivotChange(mPosition, oldStart, oldEnd,
|
||||
nsIAccessiblePivot::REASON_TEXT);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -636,6 +778,48 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HyperTextAccessible*
|
||||
nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward)
|
||||
{
|
||||
Accessible* root = GetActiveRoot();
|
||||
Accessible* accessible = aAccessible;
|
||||
while (true) {
|
||||
Accessible* child = nullptr;
|
||||
|
||||
while ((child = (aBackward ? accessible->LastChild() :
|
||||
accessible->FirstChild()))) {
|
||||
accessible = child;
|
||||
if (child->IsHyperText())
|
||||
return child->AsHyperText();
|
||||
}
|
||||
|
||||
Accessible* sibling = nullptr;
|
||||
Accessible* temp = accessible;
|
||||
do {
|
||||
if (temp == root)
|
||||
break;
|
||||
|
||||
if (temp != aAccessible && temp->IsHyperText())
|
||||
return temp->AsHyperText();
|
||||
|
||||
sibling = aBackward ? temp->PrevSibling() : temp->NextSibling();
|
||||
|
||||
if (sibling)
|
||||
break;
|
||||
} while ((temp = temp->Parent()));
|
||||
|
||||
if (!sibling)
|
||||
break;
|
||||
|
||||
accessible = sibling;
|
||||
if (accessible->IsHyperText())
|
||||
return accessible->AsHyperText();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition,
|
||||
int32_t aOldStart, int32_t aOldEnd,
|
||||
|
@ -72,6 +72,12 @@ private:
|
||||
bool aSearchCurrent,
|
||||
nsresult* aResult);
|
||||
|
||||
/*
|
||||
* Search in preorder for the first text accessible.
|
||||
*/
|
||||
mozilla::a11y::HyperTextAccessible* SearchForText(Accessible* aAccessible,
|
||||
bool aBackward);
|
||||
|
||||
/*
|
||||
* Get the effective root for this pivot, either the true root or modal root.
|
||||
*/
|
||||
|
@ -593,14 +593,8 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
||||
// addTextOffset, to put us after the embedded object char. We'll only treat the offset as
|
||||
// before the embedded object char if we end at the very beginning of the child.
|
||||
addTextOffset = addTextOffset > 0;
|
||||
}
|
||||
else {
|
||||
// Start offset, inclusive
|
||||
// Make sure the offset lands on the embedded object character in order to indicate
|
||||
// the true inner offset is inside the subtree for that link
|
||||
addTextOffset =
|
||||
(nsAccUtils::TextLength(descendantAcc) == addTextOffset) ? 1 : 0;
|
||||
}
|
||||
} else
|
||||
addTextOffset = 0;
|
||||
|
||||
descendantAcc = parentAcc;
|
||||
}
|
||||
|
@ -19,6 +19,8 @@ const ACCESSFU_DISABLE = 0;
|
||||
const ACCESSFU_ENABLE = 1;
|
||||
const ACCESSFU_AUTO = 2;
|
||||
|
||||
const SCREENREADER_SETTING = 'accessibility.screenreader';
|
||||
|
||||
this.AccessFu = {
|
||||
/**
|
||||
* Initialize chrome-layer accessibility functionality.
|
||||
@ -35,8 +37,15 @@ this.AccessFu = {
|
||||
Services.obs.addObserver(this, 'Accessibility:Settings', false);
|
||||
} catch (x) {
|
||||
// Not on Android
|
||||
if (Utils.MozBuildApp === 'b2g') {
|
||||
aWindow.addEventListener('ContentStart', this, false);
|
||||
if (aWindow.navigator.mozSettings) {
|
||||
let lock = aWindow.navigator.mozSettings.createLock();
|
||||
let req = lock.get(SCREENREADER_SETTING);
|
||||
req.addEventListener('success', () => {
|
||||
this._systemPref = req.result[SCREENREADER_SETTING];
|
||||
this._enableOrDisable();
|
||||
});
|
||||
aWindow.navigator.mozSettings.addObserver(
|
||||
SCREENREADER_SETTING, this.handleEvent.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,10 +65,9 @@ this.AccessFu = {
|
||||
}
|
||||
if (Utils.MozBuildApp === 'mobile/android') {
|
||||
Services.obs.removeObserver(this, 'Accessibility:Settings');
|
||||
} else if (Utils.MozBuildApp === 'b2g') {
|
||||
Utils.win.shell.contentBrowser.contentWindow.removeEventListener(
|
||||
'mozContentEvent', this);
|
||||
Utils.win.removeEventListener('ContentStart', this);
|
||||
} else if (Utils.win.navigator.mozSettings) {
|
||||
Utils.win.navigator.mozSettings.removeObserver(
|
||||
SCREENREADER_SETTING, this.handleEvent.bind(this));
|
||||
}
|
||||
delete this._activatePref;
|
||||
Utils.uninit();
|
||||
@ -308,20 +316,6 @@ this.AccessFu = {
|
||||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'ContentStart':
|
||||
{
|
||||
Utils.win.shell.contentBrowser.contentWindow.addEventListener(
|
||||
'mozContentEvent', this, false, true);
|
||||
break;
|
||||
}
|
||||
case 'mozContentEvent':
|
||||
{
|
||||
if (aEvent.detail.type == 'accessibility-screenreader') {
|
||||
this._systemPref = aEvent.detail.enabled;
|
||||
this._enableOrDisable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'TabOpen':
|
||||
{
|
||||
let mm = Utils.getMessageManager(aEvent.target);
|
||||
@ -351,6 +345,15 @@ this.AccessFu = {
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// A settings change, it does not have an event type
|
||||
if (aEvent.settingName == SCREENREADER_SETTING) {
|
||||
this._systemPref = aEvent.settingValue;
|
||||
this._enableOrDisable();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1541,10 +1541,12 @@ function moveToTextStart(aID)
|
||||
/**
|
||||
* Move the caret in text accessible.
|
||||
*/
|
||||
function moveCaretToDOMPoint(aID, aNode, aOffset, aExpectedOffset,
|
||||
aFocusTargetID)
|
||||
function moveCaretToDOMPoint(aID, aDOMPointNodeID, aDOMPointOffset,
|
||||
aExpectedOffset, aFocusTargetID,
|
||||
aCheckFunc)
|
||||
{
|
||||
this.target = getAccessible(aID, [nsIAccessibleText]);
|
||||
this.DOMPointNode = getNode(aDOMPointNodeID);
|
||||
this.focus = aFocusTargetID ? getAccessible(aFocusTargetID) : null;
|
||||
this.focusNode = this.focus ? this.focus.DOMNode : null;
|
||||
|
||||
@ -1553,13 +1555,25 @@ function moveCaretToDOMPoint(aID, aNode, aOffset, aExpectedOffset,
|
||||
if (this.focusNode)
|
||||
this.focusNode.focus();
|
||||
|
||||
window.getSelection().getRangeAt(0).setStart(aNode, aOffset);
|
||||
var selection = this.DOMPointNode.ownerDocument.defaultView.getSelection();
|
||||
var selRange = selection.getRangeAt(0);
|
||||
selRange.setStart(this.DOMPointNode, aDOMPointOffset);
|
||||
selRange.collapse(true);
|
||||
|
||||
selection.removeRange(selRange);
|
||||
selection.addRange(selRange);
|
||||
}
|
||||
|
||||
this.getID = function moveCaretToDOMPoint_getID()
|
||||
{
|
||||
return "Set caret on " + prettyName(aID) + " at point: " +
|
||||
prettyName(aNode) + " node with offset " + aOffset;
|
||||
prettyName(aDOMPointNodeID) + " node with offset " + aDOMPointOffset;
|
||||
}
|
||||
|
||||
this.finalCheck = function moveCaretToDOMPoint_finalCheck()
|
||||
{
|
||||
if (aCheckFunc)
|
||||
aCheckFunc.call();
|
||||
}
|
||||
|
||||
this.eventSeq = [
|
||||
|
@ -5,8 +5,25 @@
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="start-block">This is the very beginning.</div>
|
||||
<p id="paragraph-1">
|
||||
This <b>is</b> <a href="#">the</a> test of text.
|
||||
This <b>is</b> <a id="p1-link-1" href="#">the</a> test of text.
|
||||
</p>
|
||||
<div id="section-1">A <a id="s1-link-1" href="#">multiword link</a> is here. <a id="s1-link-2" href="#">We</a> will traverse</div>
|
||||
<div id="section-2">into, out, and between the subtrees.</div>
|
||||
<p id="paragraph-2">Singularity.</p>
|
||||
<table>
|
||||
<tr>
|
||||
<td id="cell-1">Magical</td>
|
||||
<td id="cell-2">unicorns</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="cell-3">and wizards</td>
|
||||
<td id="cell-4">really exist.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div id="section-3">Endless fun!</div>
|
||||
<p id="paragraph-3">Objects<a id="p3-link-1" href="#">adjacent</a>to <a id="p3-link-2" href="#">each</a><a id="p3-link-3" href="#">other</a> should be separate.</p>
|
||||
<div id="end-block">End!</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -38,7 +38,6 @@
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('paragraph-1'))));
|
||||
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [4,5],
|
||||
@ -47,15 +46,170 @@
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [5,7],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [8,9],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3],
|
||||
getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [8,9],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3],
|
||||
getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [5,7],
|
||||
getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('section-1'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,1],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,9],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,6],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,12],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,2],
|
||||
getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [20,28],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5],
|
||||
getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [6,10],
|
||||
getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5],
|
||||
getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [20,28],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [15,19],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2],
|
||||
getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,12],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,6],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [10,14],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,9],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,1],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('s1-link-1'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [1,2],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [0,1],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [2,9],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [3,4],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [13,14],
|
||||
getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('section-2'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [27,28],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1],
|
||||
getAccessible(doc.getElementById('section-2'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('paragraph-2'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,12],
|
||||
getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('cell-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8],
|
||||
getAccessible(doc.getElementById('cell-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3],
|
||||
getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,11],
|
||||
getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,6],
|
||||
getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,13],
|
||||
getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('section-3'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('section-3'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('section-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,13],
|
||||
getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,6],
|
||||
getAccessible(doc.getElementById('cell-4'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,11],
|
||||
getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3],
|
||||
getAccessible(doc.getElementById('cell-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8],
|
||||
getAccessible(doc.getElementById('cell-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('cell-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,12],
|
||||
getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('paragraph-3'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8],
|
||||
getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [8,10],
|
||||
getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
|
||||
getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5],
|
||||
getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [14,20],
|
||||
getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5],
|
||||
getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,4],
|
||||
getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [8,10],
|
||||
getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8],
|
||||
getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7],
|
||||
getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('s1-link-2'))));
|
||||
// Start with the pivot in the middle of the paragraph
|
||||
gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, " will traverse"));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19],
|
||||
getAccessible(doc.getElementById('section-1'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2],
|
||||
getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText)));
|
||||
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('end-block'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
|
||||
getAccessible(doc.getElementById('end-block'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, null, false));
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('start-block'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4],
|
||||
getAccessible(doc.getElementById('start-block'), nsIAccessibleText)));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
|
||||
gQueue.push(new setVCPosInvoker(docAcc, null, null,
|
||||
getAccessible(doc.getElementById('start-block'))));
|
||||
gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false));
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,29 @@
|
||||
"Wrong caret offset for " + aID);
|
||||
}
|
||||
|
||||
function testCaretOffsets(aList)
|
||||
{
|
||||
for (var i = 0; i < aList.length; i++)
|
||||
testCaretOffset(aList[0][0], aList[0][1]);
|
||||
}
|
||||
|
||||
function queueTraversalList(aList, aFocusNode)
|
||||
{
|
||||
for (var i = 0 ; i < aList.length; i++) {
|
||||
var node = aList[i].DOMPoint[0];
|
||||
var nodeOffset = aList[i].DOMPoint[1];
|
||||
|
||||
var textAcc = aList[i].point[0];
|
||||
var textOffset = aList[i].point[1];
|
||||
var textList = aList[i].pointList;
|
||||
var invoker =
|
||||
new moveCaretToDOMPoint(textAcc, node, nodeOffset, textOffset,
|
||||
((i == 0) ? aFocusNode : null),
|
||||
testCaretOffsets.bind(null, textList))
|
||||
gQueue.push(invoker);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Do tests.
|
||||
*/
|
||||
@ -48,7 +71,7 @@
|
||||
turnCaretBrowsing(true);
|
||||
|
||||
// test caret offsets
|
||||
testCaretOffset(document, 14);
|
||||
testCaretOffset(document, 16);
|
||||
testCaretOffset("textbox", -1);
|
||||
testCaretOffset("textarea", -1);
|
||||
testCaretOffset("p", -1);
|
||||
@ -59,6 +82,49 @@
|
||||
gQueue.push(new setCaretOffset("textbox", 1, "textbox"));
|
||||
gQueue.push(new setCaretOffset("link", 1, "link"));
|
||||
gQueue.push(new setCaretOffset("heading", 1, document));
|
||||
|
||||
// a*b*c
|
||||
var p2Doc = getNode("p2_container").contentDocument;
|
||||
var traversalList = [
|
||||
{ // before 'a'
|
||||
DOMPoint: [ getNode("p2", p2Doc).firstChild, 0 ],
|
||||
point: [ getNode("p2", p2Doc), 0 ],
|
||||
pointList: [ [ p2Doc, 0 ] ]
|
||||
},
|
||||
{ // after 'a' (before anchor)
|
||||
DOMPoint: [ getNode("p2", p2Doc).firstChild, 1 ],
|
||||
point: [ getNode("p2", p2Doc), 1 ],
|
||||
pointList: [ [ p2Doc, 0 ] ]
|
||||
},
|
||||
{ // before 'b' (inside anchor)
|
||||
DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 0 ],
|
||||
point: [ getNode("p2_a", p2Doc), 0 ],
|
||||
pointList: [
|
||||
[ getNode("p2", p2Doc), 1 ],
|
||||
[ p2Doc, 0 ]
|
||||
]
|
||||
},
|
||||
{ // after 'b' (inside anchor)
|
||||
DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 1 ],
|
||||
point: [ getNode("p2_a", p2Doc), 1 ],
|
||||
pointList: [
|
||||
[ getNode("p2", p2Doc), 1 ] ,
|
||||
[ p2Doc, 0 ]
|
||||
]
|
||||
},
|
||||
{ // before 'c' (after anchor)
|
||||
DOMPoint: [ getNode("p2", p2Doc).lastChild, 0 ],
|
||||
point: [ getNode("p2", p2Doc), 2 ],
|
||||
pointList: [ [ p2Doc, 0 ] ]
|
||||
},
|
||||
{ // after 'c'
|
||||
DOMPoint: [ getNode("p2", p2Doc).lastChild, 1 ],
|
||||
point: [ getNode("p2", p2Doc), 3 ],
|
||||
pointList: [ [ p2Doc, 0 ] ]
|
||||
}
|
||||
];
|
||||
queueTraversalList(traversalList, getNode("p2", p2Doc));
|
||||
|
||||
gQueue.onFinish = function()
|
||||
{
|
||||
turnCaretBrowsing(false);
|
||||
@ -77,22 +143,27 @@
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=448744"
|
||||
title="caretOffset should return -1 if the system caret is not currently with in that particular object">
|
||||
Mozilla Bug 448744
|
||||
Bug 448744
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=524115"
|
||||
title="HyperText accessible should get focus when the caret is positioned inside of it, text is changed or copied into clipboard by ATs">
|
||||
Mozilla Bug 524115
|
||||
Bug 524115
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=546068"
|
||||
title="Position is not being updated when atk_text_set_caret_offset is used">
|
||||
Mozilla Bug 546068
|
||||
Bug 546068
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=672717"
|
||||
title="Broken caret when moving into/out of embedded objects with right arrow">
|
||||
Bug 672717
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=725581"
|
||||
title="caretOffset for textarea should be -1 when textarea doesn't have a focus">
|
||||
Mozilla Bug 725581
|
||||
Bug 725581
|
||||
</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
@ -104,6 +175,8 @@
|
||||
<p id="p" contentEditable="true"><span>text</span><br/>text</p>
|
||||
<a id="link" href="about:">about mozilla</a>
|
||||
<h5 id="heading">heading</h5>
|
||||
<iframe id="p2_container"
|
||||
src="data:text/html,<p id='p2' contentEditable='true'>a<a id='p2_a' href='mozilla.org'>b</a>c</p>"></iframe>
|
||||
|
||||
<div id="eventdump"></div>
|
||||
</body>
|
||||
|
@ -135,6 +135,11 @@ pref("browser.search.suggest.enabled", true);
|
||||
// tell the search service that we don't really expose the "current engine"
|
||||
pref("browser.search.noCurrentEngine", true);
|
||||
|
||||
// Enable sparse localization by setting a few package locale overrides
|
||||
pref("chrome.override_package.global", "b2g-l10n");
|
||||
pref("chrome.override_package.mozapps", "b2g-l10n");
|
||||
pref("chrome.override_package.passwordmgr", "b2g-l10n");
|
||||
|
||||
// enable xul error pages
|
||||
pref("browser.xul.error_pages.enabled", true);
|
||||
|
||||
|
@ -74,7 +74,6 @@ libs-%:
|
||||
|
||||
# Tailored target to just add the chrome processing for multi-locale builds
|
||||
chrome-%:
|
||||
@$(MAKE) -C ../../toolkit/locales chrome-$*
|
||||
@$(MAKE) chrome AB_CD=$*
|
||||
@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$*
|
||||
|
||||
|
@ -13,3 +13,61 @@
|
||||
* locale/@AB_CD@/b2g-l10n/netError.dtd (%chrome/overrides/netError.dtd)
|
||||
* locale/@AB_CD@/b2g-l10n/aboutCertError.dtd (%chrome/overrides/aboutCertError.dtd)
|
||||
* locale/@AB_CD@/b2g-l10n/appstrings.properties (%chrome/overrides/appstrings.properties)
|
||||
|
||||
|
||||
# overrides for toolkit l10n, also for en-US
|
||||
relativesrcdir toolkit/locales:
|
||||
locale/@AB_CD@/b2g-l10n/overrides/about.dtd (%chrome/global/about.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/aboutAbout.dtd (%chrome/global/aboutAbout.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/aboutRights.dtd (%chrome/global/aboutRights.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/commonDialogs.properties (%chrome/global/commonDialogs.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/handling/handling.properties (%chrome/mozapps/handling/handling.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/intl.properties (%chrome/global/intl.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/intl.css (%chrome/global/intl.css)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/search/search.properties (%chrome/search/search.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/update/updates.properties (%chrome/mozapps/update/updates.properties)
|
||||
# about:support
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutSupport.dtd (%chrome/global/aboutSupport.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutSupport.properties (%chrome/global/aboutSupport.properties)
|
||||
#about:crashes
|
||||
locale/@AB_CD@/b2g-l10n/overrides/crashreporter/crashes.dtd (%crashreporter/crashes.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/crashreporter/crashes.properties (%crashreporter/crashes.properties)
|
||||
#about:mozilla
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/mozilla.dtd (%chrome/global/mozilla.dtd)
|
||||
#about:telemetry
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.dtd (%chrome/global/aboutTelemetry.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.properties (%chrome/global/aboutTelemetry.properties)
|
||||
|
||||
% override chrome://global/locale/about.dtd chrome://b2g-l10n/locale/overrides/about.dtd
|
||||
% override chrome://global/locale/aboutAbout.dtd chrome://b2g-l10n/locale/overrides/aboutAbout.dtd
|
||||
% override chrome://global/locale/aboutRights.dtd chrome://b2g-l10n/locale/overrides/aboutRights.dtd
|
||||
% override chrome://global/locale/commonDialogs.properties chrome://b2g-l10n/locale/overrides/commonDialogs.properties
|
||||
% override chrome://mozapps/locale/handling/handling.properties chrome://b2g-l10n/locale/overrides/handling/handling.properties
|
||||
% override chrome://global/locale/intl.properties chrome://b2g-l10n/locale/overrides/intl.properties
|
||||
% override chrome://global/locale/intl.css chrome://b2g-l10n/locale/overrides/intl.css
|
||||
% override chrome://passwordmgr/locale/passwordmgr.properties chrome://b2g-l10n/locale/overrides/passwordmgr/passwordmgr.properties
|
||||
% override chrome://global/locale/search/search.properties chrome://b2g-l10n/locale/overrides/search/search.properties
|
||||
% override chrome://mozapps/locale/update/updates.properties chrome://b2g-l10n/locale/overrides/update/updates.properties
|
||||
% override chrome://global/locale/aboutSupport.dtd chrome://b2g-l10n/locale/overrides/global/aboutSupport.dtd
|
||||
% override chrome://global/locale/aboutSupport.properties chrome://b2g-l10n/locale/overrides/global/aboutSupport.properties
|
||||
% override chrome://global/locale/crashes.dtd chrome://b2g-l10n/locale/overrides/crashreporter/crashes.dtd
|
||||
% override chrome://global/locale/crashes.properties chrome://b2g-l10n/locale/overrides/crashreporter/crashes.properties
|
||||
% override chrome://global/locale/mozilla.dtd chrome://b2g-l10n/locale/overrides/global/mozilla.dtd
|
||||
% override chrome://global/locale/aboutTelemetry.dtd chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.dtd
|
||||
% override chrome://global/locale/aboutTelemetry.properties chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.properties
|
||||
|
||||
# overrides for dom l10n, also for en-US
|
||||
relativesrcdir dom/locales:
|
||||
locale/@AB_CD@/b2g-l10n/overrides/charsetTitles.properties (%chrome/charsetTitles.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/global.dtd (%chrome/global.dtd)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/AccessFu.properties (%chrome/accessibility/AccessFu.properties)
|
||||
locale/@AB_CD@/b2g-l10n/overrides/dom/dom.properties (%chrome/dom/dom.properties)
|
||||
#about:plugins
|
||||
locale/@AB_CD@/b2g-l10n/overrides/plugins.properties (%chrome/plugins.properties)
|
||||
|
||||
% override chrome://global/locale/charsetTitles.properties chrome://b2g-l10n/locale/overrides/charsetTitles.properties
|
||||
% override chrome://global/locale/global.dtd chrome://b2g-l10n/locale/overrides/global.dtd
|
||||
% override chrome://global/locale/AccessFu.properties chrome://b2g-l10n/locale/overrides/AccessFu.properties
|
||||
% override chrome://global/locale/dom/dom.properties chrome://b2g-l10n/locale/overrides/dom/dom.properties
|
||||
% override chrome://global/locale/plugins.properties chrome://b2g-l10n/locale/overrides/plugins.properties
|
||||
|
@ -1119,18 +1119,25 @@ var gBrowserInit = {
|
||||
// If the user manually opens the download manager before the timeout, the
|
||||
// downloads will start right away, and getting the service again won't hurt.
|
||||
setTimeout(function() {
|
||||
let DownloadsCommon =
|
||||
Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
|
||||
if (DownloadsCommon.useJSTransfer) {
|
||||
// Open the data link without initalizing nsIDownloadManager.
|
||||
DownloadsCommon.initializeAllDataLinks();
|
||||
} else {
|
||||
// Initalizing nsIDownloadManager will trigger the data link.
|
||||
Services.downloads;
|
||||
try {
|
||||
let DownloadsCommon =
|
||||
Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon;
|
||||
if (DownloadsCommon.useJSTransfer) {
|
||||
// Open the data link without initalizing nsIDownloadManager.
|
||||
DownloadsCommon.initializeAllDataLinks();
|
||||
let DownloadsTaskbar =
|
||||
Cu.import("resource:///modules/DownloadsTaskbar.jsm", {}).DownloadsTaskbar;
|
||||
DownloadsTaskbar.registerIndicator(window);
|
||||
} else {
|
||||
// Initalizing nsIDownloadManager will trigger the data link.
|
||||
Services.downloads;
|
||||
let DownloadTaskbarProgress =
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
|
||||
DownloadTaskbarProgress.onBrowserWindowLoad(window);
|
||||
}
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
}
|
||||
let DownloadTaskbarProgress =
|
||||
Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress;
|
||||
DownloadTaskbarProgress.onBrowserWindowLoad(window);
|
||||
}, 10000);
|
||||
|
||||
// The object handling the downloads indicator is also initialized here in the
|
||||
|
180
browser/components/downloads/src/DownloadsTaskbar.jsm
Normal file
180
browser/components/downloads/src/DownloadsTaskbar.jsm
Normal file
@ -0,0 +1,180 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
/* 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/. */
|
||||
|
||||
/**
|
||||
* Handles the download progress indicator in the taskbar.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"DownloadsTaskbar",
|
||||
];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
|
||||
if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
|
||||
return null;
|
||||
}
|
||||
let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
|
||||
.getService(Ci.nsIWinTaskbar);
|
||||
return winTaskbar.available && winTaskbar;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
|
||||
return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
|
||||
Cc["@mozilla.org/widget/macdocksupport;1"]
|
||||
.getService(Ci.nsITaskbarProgress);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsTaskbar
|
||||
|
||||
/**
|
||||
* Handles the download progress indicator in the taskbar.
|
||||
*/
|
||||
this.DownloadsTaskbar = {
|
||||
/**
|
||||
* Underlying DownloadSummary providing the aggregate download information, or
|
||||
* null if the indicator has never been initialized.
|
||||
*/
|
||||
_summary: null,
|
||||
|
||||
/**
|
||||
* nsITaskbarProgress object to which download information is dispatched.
|
||||
* This can be null if the indicator has never been initialized or if the
|
||||
* indicator is currently hidden on Windows.
|
||||
*/
|
||||
_taskbarProgress: null,
|
||||
|
||||
/**
|
||||
* This method is called after a new browser window is opened, and ensures
|
||||
* that the download progress indicator is displayed in the taskbar.
|
||||
*
|
||||
* On Windows, the indicator is attached to the first browser window that
|
||||
* calls this method. When the window is closed, the indicator is moved to
|
||||
* another browser window, if available, in no particular order. When there
|
||||
* are no browser windows visible, the indicator is hidden.
|
||||
*
|
||||
* On Mac OS X, the indicator is initialized globally when this method is
|
||||
* called for the first time. Subsequent calls have no effect.
|
||||
*
|
||||
* @param aBrowserWindow
|
||||
* nsIDOMWindow object of the newly opened browser window to which the
|
||||
* indicator may be attached.
|
||||
*/
|
||||
registerIndicator: function (aBrowserWindow)
|
||||
{
|
||||
if (!this._taskbarProgress) {
|
||||
if (gMacTaskbarProgress) {
|
||||
// On Mac OS X, we have to register the global indicator only once.
|
||||
this._taskbarProgress = gMacTaskbarProgress;
|
||||
// Free the XPCOM reference on shutdown, to prevent detecting a leak.
|
||||
Services.obs.addObserver(() => {
|
||||
this._taskbarProgress = null;
|
||||
gMacTaskbarProgress = null;
|
||||
}, "quit-application-granted", false);
|
||||
} else if (gWinTaskbar) {
|
||||
// On Windows, the indicator is currently hidden because we have no
|
||||
// previous browser window, thus we should attach the indicator now.
|
||||
this._attachIndicator(aBrowserWindow);
|
||||
} else {
|
||||
// The taskbar indicator is not available on this platform.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that the DownloadSummary object will be created asynchronously.
|
||||
if (!this._summary) {
|
||||
Downloads.getSummary(Downloads.ALL).then(summary => {
|
||||
// In case the method is re-entered, we simply ignore redundant
|
||||
// invocations of the callback, instead of keeping separate state.
|
||||
if (this._summary) {
|
||||
return;
|
||||
}
|
||||
this._summary = summary;
|
||||
return this._summary.addView(this);
|
||||
}).then(null, Cu.reportError);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* On Windows, attaches the taskbar indicator to the specified browser window.
|
||||
*/
|
||||
_attachIndicator: function (aWindow)
|
||||
{
|
||||
// Activate the indicator on the specified window.
|
||||
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIXULWindow).docShell;
|
||||
this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell);
|
||||
|
||||
// If the DownloadSummary object has already been created, we should update
|
||||
// the state of the new indicator, otherwise it will be updated as soon as
|
||||
// the DownloadSummary view is registered.
|
||||
if (this._summary) {
|
||||
this.onSummaryChanged();
|
||||
}
|
||||
|
||||
aWindow.addEventListener("unload", () => {
|
||||
// Locate another browser window, excluding the one being closed.
|
||||
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
|
||||
if (browserWindow) {
|
||||
// Move the progress indicator to the other browser window.
|
||||
this._attachIndicator(browserWindow);
|
||||
} else {
|
||||
// The last browser window has been closed. We remove the reference to
|
||||
// the taskbar progress object so that the indicator will be registered
|
||||
// again on the next browser window that is opened.
|
||||
this._taskbarProgress = null;
|
||||
}
|
||||
}, false);
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadSummary view
|
||||
|
||||
onSummaryChanged: function ()
|
||||
{
|
||||
// If the last browser window has been closed, we have no indicator anymore.
|
||||
if (!this._taskbarProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
|
||||
this._taskbarProgress.setProgressState(
|
||||
Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0);
|
||||
} else {
|
||||
// For a brief moment before completion, some download components may
|
||||
// report more transferred bytes than the total number of bytes. Thus,
|
||||
// ensure that we never break the expectations of the progress indicator.
|
||||
let progressCurrentBytes = Math.min(this._summary.progressTotalBytes,
|
||||
this._summary.progressCurrentBytes);
|
||||
this._taskbarProgress.setProgressState(
|
||||
Ci.nsITaskbarProgress.STATE_NORMAL,
|
||||
progressCurrentBytes,
|
||||
this._summary.progressTotalBytes);
|
||||
}
|
||||
},
|
||||
};
|
@ -13,5 +13,6 @@ EXTRA_COMPONENTS += [
|
||||
EXTRA_JS_MODULES += [
|
||||
'DownloadsCommon.jsm',
|
||||
'DownloadsLogger.jsm',
|
||||
'DownloadsTaskbar.jsm',
|
||||
]
|
||||
|
||||
|
@ -215,34 +215,27 @@
|
||||
accesskey="&newWindowsAsTabs.accesskey;"
|
||||
preference="browser.link.open_newwindow"
|
||||
onsyncfrompreference="return gMainPane.readLinkTarget();"
|
||||
onsynctopreference="return gMainPane.writeLinkTarget();"
|
||||
class="indent"/>
|
||||
onsynctopreference="return gMainPane.writeLinkTarget();"/>
|
||||
|
||||
<checkbox id="warnCloseMultiple" label="&warnCloseMultipleTabs.label;"
|
||||
accesskey="&warnCloseMultipleTabs.accesskey;"
|
||||
preference="browser.tabs.warnOnClose"
|
||||
class="indent"/>
|
||||
preference="browser.tabs.warnOnClose"/>
|
||||
|
||||
<checkbox id="warnOpenMany" label="&warnOpenManyTabs.label;"
|
||||
accesskey="&warnOpenManyTabs.accesskey;"
|
||||
preference="browser.tabs.warnOnOpen"
|
||||
class="indent"/>
|
||||
preference="browser.tabs.warnOnOpen"/>
|
||||
|
||||
<checkbox id="restoreOnDemand" label="&restoreTabsOnDemand.label;"
|
||||
accesskey="&restoreTabsOnDemand.accesskey;"
|
||||
preference="browser.sessionstore.restore_on_demand"
|
||||
class="indent"/>
|
||||
preference="browser.sessionstore.restore_on_demand"/>
|
||||
|
||||
<checkbox id="switchToNewTabs" label="&switchToNewTabs.label;"
|
||||
accesskey="&switchToNewTabs.accesskey;"
|
||||
preference="browser.tabs.loadInBackground"
|
||||
class="indent"/>
|
||||
preference="browser.tabs.loadInBackground"/>
|
||||
|
||||
#ifdef XP_WIN
|
||||
<checkbox id="showTabsInTaskbar" label="&showTabsInTaskbar.label;"
|
||||
accesskey="&showTabsInTaskbar.accesskey;"
|
||||
preference="browser.taskbar.previews.enable"
|
||||
class="indent"/>
|
||||
preference="browser.taskbar.previews.enable"/>
|
||||
#endif
|
||||
|
||||
</groupbox>
|
||||
|
@ -22,6 +22,7 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_dbg_breakpoints-pane.js \
|
||||
browser_dbg_chrome-debugging.js \
|
||||
browser_dbg_clean-exit.js \
|
||||
browser_dbg_clean-exit-window.js \
|
||||
browser_dbg_cmd-blackbox.js \
|
||||
browser_dbg_cmd-break.js \
|
||||
browser_dbg_cmd-dbg.js \
|
||||
|
@ -0,0 +1,90 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that closing a window with the debugger in a paused state exits cleanly.
|
||||
*/
|
||||
|
||||
let gDebuggee, gPanel, gDebugger, gWindow;
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html";
|
||||
|
||||
function test() {
|
||||
addWindow(TAB_URL)
|
||||
.then(win => initDebugger(TAB_URL, win))
|
||||
.then(([aTab, aDebuggee, aPanel, aWindow]) => {
|
||||
gDebuggee = aDebuggee;
|
||||
gPanel = aPanel;
|
||||
gDebugger = gPanel.panelWin;
|
||||
gWindow = aWindow;
|
||||
|
||||
return testCleanExit(gWindow);
|
||||
})
|
||||
.then(null, aError => {
|
||||
ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
|
||||
});
|
||||
}
|
||||
|
||||
function testCleanExit(aWindow) {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gWindow = aWindow;
|
||||
ok(!!gWindow, "Second window created.");
|
||||
|
||||
gWindow.focus();
|
||||
|
||||
let topWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
is(topWindow, gWindow,
|
||||
"The second window is on top.");
|
||||
|
||||
let isActive = promise.defer();
|
||||
let isLoaded = promise.defer();
|
||||
|
||||
promise.all([isActive.promise, isLoaded.promise]).then(() => {
|
||||
gWindow.BrowserChromeTest.runWhenReady(() => {
|
||||
waitForSourceAndCaretAndScopes(gPanel, ".html", 16).then(() => {
|
||||
is(gDebugger.gThreadClient.paused, true,
|
||||
"Should be paused after the debugger statement.");
|
||||
gWindow.close();
|
||||
deferred.resolve();
|
||||
finish();
|
||||
});
|
||||
|
||||
gDebuggee.runDebuggerStatement();
|
||||
});
|
||||
});
|
||||
|
||||
let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
|
||||
if (focusManager.activeWindow != gWindow) {
|
||||
gWindow.addEventListener("activate", function onActivate(aEvent) {
|
||||
if (aEvent.target != gWindow) {
|
||||
return;
|
||||
}
|
||||
gWindow.removeEventListener("activate", onActivate, true);
|
||||
isActive.resolve();
|
||||
}, true);
|
||||
} else {
|
||||
isActive.resolve();
|
||||
}
|
||||
|
||||
let contentLocation = gWindow.content.location.href;
|
||||
if (contentLocation != TAB_URL) {
|
||||
gWindow.document.addEventListener("load", function onLoad(aEvent) {
|
||||
if (aEvent.target.documentURI != TAB_URL) {
|
||||
return;
|
||||
}
|
||||
gWindow.document.removeEventListener("load", onLoad, true);
|
||||
isLoaded.resolve();
|
||||
}, true);
|
||||
} else {
|
||||
isLoaded.resolve();
|
||||
}
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
gWindow = null;
|
||||
gDebuggee = null;
|
||||
gPanel = null;
|
||||
gDebugger = null;
|
||||
});
|
@ -416,18 +416,18 @@ function backspaceText(aElement, aTimes) {
|
||||
}
|
||||
}
|
||||
|
||||
function getTab(aTarget) {
|
||||
function getTab(aTarget, aWindow) {
|
||||
if (aTarget instanceof XULElement) {
|
||||
return promise.resolve(aTarget);
|
||||
} else {
|
||||
return addTab(aTarget);
|
||||
return addTab(aTarget, aWindow);
|
||||
}
|
||||
}
|
||||
|
||||
function initDebugger(aTarget, aWindow) {
|
||||
info("Initializing a debugger panel.");
|
||||
|
||||
return getTab(aTarget).then(aTab => {
|
||||
return getTab(aTarget, aWindow).then(aTab => {
|
||||
info("Debugee tab added successfully: " + aTarget);
|
||||
|
||||
let deferred = promise.defer();
|
||||
@ -445,7 +445,7 @@ function initDebugger(aTarget, aWindow) {
|
||||
info("Debugger client resumed successfully.");
|
||||
|
||||
prepareDebugger(debuggerPanel);
|
||||
deferred.resolve([aTab, debuggee, debuggerPanel]);
|
||||
deferred.resolve([aTab, debuggee, debuggerPanel, aWindow]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -180,12 +180,6 @@ function ResponsiveUI(aWindow, aTab)
|
||||
this.buildUI();
|
||||
this.checkMenus();
|
||||
|
||||
this.docShell = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
this.docShell.deviceSizeIsPageSize = true;
|
||||
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
|
||||
this.rotate();
|
||||
@ -258,8 +252,6 @@ ResponsiveUI.prototype = {
|
||||
this.browser.removeEventListener("load", this.bound_onPageLoad, true);
|
||||
this.browser.removeEventListener("unload", this.bound_onPageUnload, true);
|
||||
|
||||
this.docShell.deviceSizeIsPageSize = false;
|
||||
|
||||
if (this._floatingScrollbars)
|
||||
switchToNativeScrollbars(this.tab);
|
||||
|
||||
@ -296,7 +288,6 @@ ResponsiveUI.prototype = {
|
||||
this.container.removeAttribute("responsivemode");
|
||||
this.stack.removeAttribute("responsivemode");
|
||||
|
||||
delete this.docShell;
|
||||
delete this.tab.__responsiveUI;
|
||||
if (this.touchEventHandler)
|
||||
this.touchEventHandler.stop();
|
||||
|
@ -9,7 +9,6 @@ MOCHITEST_BROWSER_FILES := \
|
||||
browser_responsive_cmd.js \
|
||||
browser_responsivecomputedview.js \
|
||||
browser_responsiveui_touch.js \
|
||||
browser_responsive_devicewidth.js \
|
||||
touch.html \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
@ -1,61 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test() {
|
||||
let instance;
|
||||
let mgr = ResponsiveUI.ResponsiveUIManager;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(startTest, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,mop";
|
||||
|
||||
function startTest() {
|
||||
mgr.once("on", function() {executeSoon(onUIOpen)});
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
}
|
||||
|
||||
function onUIOpen() {
|
||||
instance = gBrowser.selectedTab.__responsiveUI;
|
||||
instance.stack.setAttribute("notransition", "true");
|
||||
ok(instance, "instance of the module is attached to the tab.");
|
||||
|
||||
let mql = content.matchMedia("(max-device-width:100px)")
|
||||
|
||||
ok(!mql.matches, "media query doesn't match.");
|
||||
|
||||
mql.addListener(onMediaChange);
|
||||
instance.setSize(90, 500);
|
||||
}
|
||||
|
||||
function onMediaChange(mql) {
|
||||
mql.removeListener(onMediaChange);
|
||||
ok(mql.matches, "media query matches.");
|
||||
ok(window.screen.width != content.screen.width, "screen.width is not the size of the screen.");
|
||||
is(content.screen.width, 90, "screen.width is the width of the page.");
|
||||
is(content.screen.height, 500, "screen.height is the height of the page.");
|
||||
|
||||
|
||||
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
mql.addListener(onMediaChange2);
|
||||
docShell.deviceSizeIsPageSize = false;
|
||||
}
|
||||
|
||||
function onMediaChange2(mql) {
|
||||
mql.removeListener(onMediaChange);
|
||||
ok(!mql.matches, "media query has been re-evaluated.");
|
||||
ok(window.screen.width == content.screen.width, "screen.width is not the size of the screen.");
|
||||
instance.stack.removeAttribute("notransition");
|
||||
document.getElementById("Tools:ResponsiveUI").doCommand();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -668,16 +668,13 @@ PropertyView.prototype = {
|
||||
|
||||
/**
|
||||
* Returns the className that should be assigned to the propertyView.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
get propertyHeaderClassName()
|
||||
{
|
||||
if (this.visible) {
|
||||
this.tree._darkStripe = !this.tree._darkStripe;
|
||||
let darkValue = this.tree._darkStripe ?
|
||||
"property-view theme-bg-darker" : "property-view";
|
||||
return darkValue;
|
||||
let isDark = this.tree._darkStripe = !this.tree._darkStripe;
|
||||
return isDark ? "property-view theme-bg-darker" : "property-view";
|
||||
}
|
||||
return "property-view-hidden";
|
||||
},
|
||||
@ -690,49 +687,66 @@ PropertyView.prototype = {
|
||||
get propertyContentClassName()
|
||||
{
|
||||
if (this.visible) {
|
||||
let darkValue = this.tree._darkStripe ?
|
||||
"property-content theme-bg-darker" : "property-content";
|
||||
return darkValue;
|
||||
let isDark = this.tree._darkStripe;
|
||||
return isDark ? "property-content theme-bg-darker" : "property-content";
|
||||
}
|
||||
return "property-content-hidden";
|
||||
},
|
||||
|
||||
/**
|
||||
* Build the markup for on computed style
|
||||
* @return Element
|
||||
*/
|
||||
buildMain: function PropertyView_buildMain()
|
||||
{
|
||||
let doc = this.tree.styleDocument;
|
||||
let onToggle = this.onStyleToggle.bind(this);
|
||||
|
||||
// Build the container element
|
||||
this.element = doc.createElementNS(HTML_NS, "div");
|
||||
this.element.setAttribute("class", this.propertyHeaderClassName);
|
||||
|
||||
this.matchedExpander = doc.createElementNS(HTML_NS, "div");
|
||||
this.matchedExpander.className = "expander theme-twisty";
|
||||
this.matchedExpander.setAttribute("tabindex", "0");
|
||||
this.matchedExpander.addEventListener("click",
|
||||
this.matchedExpanderClick.bind(this), false);
|
||||
this.matchedExpander.addEventListener("keydown", function(aEvent) {
|
||||
// Make it keyboard navigable
|
||||
this.element.setAttribute("tabindex", "0");
|
||||
this.element.addEventListener("keydown", function(aEvent) {
|
||||
let keyEvent = Ci.nsIDOMKeyEvent;
|
||||
if (aEvent.keyCode == keyEvent.DOM_VK_F1) {
|
||||
this.mdnLinkClick();
|
||||
}
|
||||
if (aEvent.keyCode == keyEvent.DOM_VK_RETURN ||
|
||||
aEvent.keyCode == keyEvent.DOM_VK_SPACE) {
|
||||
this.matchedExpanderClick(aEvent);
|
||||
onToggle(aEvent);
|
||||
}
|
||||
}.bind(this), false);
|
||||
|
||||
// Build the twisty expand/collapse
|
||||
this.matchedExpander = doc.createElementNS(HTML_NS, "div");
|
||||
this.matchedExpander.className = "expander theme-twisty";
|
||||
this.matchedExpander.addEventListener("click", onToggle, false);
|
||||
this.element.appendChild(this.matchedExpander);
|
||||
|
||||
// Build the style name element
|
||||
this.nameNode = doc.createElementNS(HTML_NS, "div");
|
||||
this.element.appendChild(this.nameNode);
|
||||
this.nameNode.setAttribute("class", "property-name theme-fg-color5");
|
||||
// Reset its tabindex attribute otherwise, if an ellipsis is applied
|
||||
// it will be reachable via TABing
|
||||
this.nameNode.setAttribute("tabindex", "");
|
||||
this.nameNode.textContent = this.nameNode.title = this.name;
|
||||
this.nameNode.addEventListener("click", function(aEvent) {
|
||||
this.matchedExpander.focus();
|
||||
}.bind(this), false);
|
||||
// Make it hand over the focus to the container
|
||||
this.nameNode.addEventListener("click", () => this.element.focus(), false);
|
||||
this.element.appendChild(this.nameNode);
|
||||
|
||||
// Build the style value element
|
||||
this.valueNode = doc.createElementNS(HTML_NS, "div");
|
||||
this.element.appendChild(this.valueNode);
|
||||
this.valueNode.setAttribute("class", "property-value theme-fg-color1");
|
||||
// Reset its tabindex attribute otherwise, if an ellipsis is applied
|
||||
// it will be reachable via TABing
|
||||
this.valueNode.setAttribute("tabindex", "");
|
||||
this.valueNode.setAttribute("dir", "ltr");
|
||||
this.valueNode.textContent = this.valueNode.title = this.value;
|
||||
// Make it hand over the focus to the container
|
||||
this.valueNode.addEventListener("click", () => this.element.focus(), false);
|
||||
this.element.appendChild(this.valueNode);
|
||||
|
||||
return this.element;
|
||||
},
|
||||
@ -836,7 +850,7 @@ PropertyView.prototype = {
|
||||
* @param {Event} aEvent Used to determine the class name of the targets click
|
||||
* event.
|
||||
*/
|
||||
matchedExpanderClick: function PropertyView_matchedExpanderClick(aEvent)
|
||||
onStyleToggle: function PropertyView_onStyleToggle(aEvent)
|
||||
{
|
||||
this.matchedExpanded = !this.matchedExpanded;
|
||||
this.refreshMatchedSelectors();
|
||||
|
@ -36,6 +36,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_bug894376_css_value_completion_existing_property_value_pair.js \
|
||||
browser_ruleview_bug_902966_revert_value_on_ESC.js \
|
||||
browser_ruleview_pseudoelement.js \
|
||||
browser_computedview_bug835808_keyboard_nav.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -0,0 +1,94 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the style inspector works properly
|
||||
|
||||
let doc, computedView, inspector;
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onBrowserLoad(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onBrowserLoad, true);
|
||||
doc = content.document;
|
||||
waitForFocus(createDocument, content);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html,computed view context menu test";
|
||||
}
|
||||
|
||||
function createDocument()
|
||||
{
|
||||
doc.body.innerHTML = '<style type="text/css"> ' +
|
||||
'span { font-variant: small-caps; color: #000000; } ' +
|
||||
'.nomatches {color: #ff0000;}</style> <div id="first" style="margin: 10em; ' +
|
||||
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA">\n' +
|
||||
'<h1>Some header text</h1>\n' +
|
||||
'<p id="salutation" style="font-size: 12pt">hi.</p>\n' +
|
||||
'<p id="body" style="font-size: 12pt">I am a test-case. This text exists ' +
|
||||
'solely to provide some things to <span style="color: yellow">' +
|
||||
'highlight</span> and <span style="font-weight: bold">count</span> ' +
|
||||
'style list-items in the box at right. If you are reading this, ' +
|
||||
'you should go do something else instead. Maybe read a book. Or better ' +
|
||||
'yet, write some test-cases for another bit of code. ' +
|
||||
'<span style="font-style: italic">some text</span></p>\n' +
|
||||
'<p id="closing">more text</p>\n' +
|
||||
'<p>even more text</p>' +
|
||||
'</div>';
|
||||
doc.title = "Computed view keyboard navigation test";
|
||||
|
||||
openComputedView(startTests);
|
||||
}
|
||||
|
||||
function startTests(aInspector, aComputedView)
|
||||
{
|
||||
computedView = aComputedView;
|
||||
inspector = aInspector;
|
||||
testTabThrougStyles();
|
||||
}
|
||||
|
||||
function endTests()
|
||||
{
|
||||
computedView = inspector = doc = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function testTabThrougStyles()
|
||||
{
|
||||
let span = doc.querySelector("span");
|
||||
|
||||
inspector.once("computed-view-refreshed", () => {
|
||||
// Selecting the first computed style in the list
|
||||
let firstStyle = computedView.styleDocument.querySelector(".property-view");
|
||||
ok(firstStyle, "First computed style found in panel");
|
||||
firstStyle.focus();
|
||||
|
||||
// Tab to select the 2nd style, press return
|
||||
EventUtils.synthesizeKey("VK_TAB", {});
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
inspector.once("computed-view-property-expanded", () => {
|
||||
// Verify the 2nd style has been expanded
|
||||
let secondStyleSelectors = computedView.styleDocument.querySelectorAll(
|
||||
".property-content .matchedselectors")[1];
|
||||
ok(secondStyleSelectors.childNodes.length > 0, "Matched selectors expanded");
|
||||
|
||||
// Tab back up and test the same thing, with space
|
||||
EventUtils.synthesizeKey("VK_TAB", {shiftKey: true});
|
||||
EventUtils.synthesizeKey("VK_SPACE", {});
|
||||
inspector.once("computed-view-property-expanded", () => {
|
||||
// Verify the 1st style has been expanded too
|
||||
let firstStyleSelectors = computedView.styleDocument.querySelectorAll(
|
||||
".property-content .matchedselectors")[0];
|
||||
ok(firstStyleSelectors.childNodes.length > 0, "Matched selectors expanded");
|
||||
|
||||
endTests();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
inspector.selection.setNode(span);
|
||||
}
|
@ -45,6 +45,7 @@ body {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.property-value {
|
||||
@ -58,6 +59,7 @@ body {
|
||||
background-size: 5px 8px;
|
||||
background-position: 2px center;
|
||||
padding-left: 10px;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.other-property-value {
|
||||
|
@ -63,6 +63,7 @@ body {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.property-value {
|
||||
@ -76,6 +77,7 @@ body {
|
||||
background-size: 5px 8px;
|
||||
background-position: 2px center;
|
||||
padding-left: 10px;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.other-property-value {
|
||||
|
@ -63,6 +63,7 @@ body {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.property-value {
|
||||
@ -76,6 +77,7 @@ body {
|
||||
background-size: 5px 8px;
|
||||
background-position: 2px center;
|
||||
padding-left: 10px;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.other-property-value {
|
||||
|
@ -683,16 +683,15 @@ class Automation(object):
|
||||
def killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo):
|
||||
"""Kill the process, preferrably in a way that gets us a stack trace."""
|
||||
if self.CRASHREPORTER and not debuggerInfo:
|
||||
if self.UNIXISH:
|
||||
if not self.IS_WIN32:
|
||||
# ABRT will get picked up by Breakpad's signal handler
|
||||
os.kill(processPID, signal.SIGABRT)
|
||||
return
|
||||
elif self.IS_WIN32:
|
||||
else:
|
||||
# We should have a "crashinject" program in our utility path
|
||||
crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
|
||||
if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(processPID)]).wait() == 0:
|
||||
return
|
||||
#TODO: kill the process such that it triggers Breakpad on OS X (bug 525296)
|
||||
self.log.info("Can't trigger Breakpad, just killing process")
|
||||
self.killPid(processPID)
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/dom/Date.h"
|
||||
#include "mozilla/dom/HTMLInputElementBinding.h"
|
||||
#include "nsAsyncDOMEvent.h"
|
||||
#include "nsAttrValueInlines.h"
|
||||
|
||||
@ -4504,6 +4503,11 @@ HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
|
||||
if (!aRv.Failed()) {
|
||||
aRv = textControlFrame->ScrollSelectionIntoView();
|
||||
}
|
||||
|
||||
nsContentUtils::DispatchTrustedEvent(OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(this),
|
||||
NS_LITERAL_STRING("select"), true,
|
||||
false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4521,6 +4525,113 @@ HTMLInputElement::SetSelectionRange(int32_t aSelectionStart,
|
||||
return rv.ErrorCode();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetRangeText(const nsAString& aReplacement, ErrorResult& aRv)
|
||||
{
|
||||
if (!SupportsSetRangeText()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t start, end;
|
||||
aRv = GetSelectionRange(&start, &end);
|
||||
if (aRv.Failed()) {
|
||||
nsTextEditorState* state = GetEditorState();
|
||||
if (state && state->IsSelectionCached()) {
|
||||
start = state->GetSelectionProperties().mStart;
|
||||
end = state->GetSelectionProperties().mEnd;
|
||||
aRv = NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
SetRangeText(aReplacement, start, end, mozilla::dom::SelectionMode::Preserve,
|
||||
aRv, start, end);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
|
||||
uint32_t aEnd, const SelectionMode& aSelectMode,
|
||||
ErrorResult& aRv, int32_t aSelectionStart,
|
||||
int32_t aSelectionEnd)
|
||||
{
|
||||
if (!SupportsSetRangeText()) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aStart > aEnd) {
|
||||
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
GetValueInternal(value);
|
||||
uint32_t inputValueLength = value.Length();
|
||||
|
||||
if (aStart > inputValueLength) {
|
||||
aStart = inputValueLength;
|
||||
}
|
||||
|
||||
if (aEnd > inputValueLength) {
|
||||
aEnd = inputValueLength;
|
||||
}
|
||||
|
||||
if (aSelectionStart == -1 && aSelectionEnd == -1) {
|
||||
aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd);
|
||||
if (aRv.Failed()) {
|
||||
nsTextEditorState* state = GetEditorState();
|
||||
if (state && state->IsSelectionCached()) {
|
||||
aSelectionStart = state->GetSelectionProperties().mStart;
|
||||
aSelectionEnd = state->GetSelectionProperties().mEnd;
|
||||
aRv = NS_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aStart < aEnd) {
|
||||
value.Replace(aStart, aEnd - aStart, aReplacement);
|
||||
SetValueInternal(value, false, false);
|
||||
}
|
||||
|
||||
uint32_t newEnd = aStart + aReplacement.Length();
|
||||
int32_t delta = aReplacement.Length() - (aEnd - aStart);
|
||||
|
||||
switch (aSelectMode) {
|
||||
case mozilla::dom::SelectionMode::Select:
|
||||
{
|
||||
aSelectionStart = aStart;
|
||||
aSelectionEnd = newEnd;
|
||||
}
|
||||
break;
|
||||
case mozilla::dom::SelectionMode::Start:
|
||||
{
|
||||
aSelectionStart = aSelectionEnd = aStart;
|
||||
}
|
||||
break;
|
||||
case mozilla::dom::SelectionMode::End:
|
||||
{
|
||||
aSelectionStart = aSelectionEnd = newEnd;
|
||||
}
|
||||
break;
|
||||
case mozilla::dom::SelectionMode::Preserve:
|
||||
{
|
||||
if ((uint32_t)aSelectionStart > aEnd)
|
||||
aSelectionStart += delta;
|
||||
else if ((uint32_t)aSelectionStart > aStart)
|
||||
aSelectionStart = aStart;
|
||||
|
||||
if ((uint32_t)aSelectionEnd > aEnd)
|
||||
aSelectionEnd += delta;
|
||||
else if ((uint32_t)aSelectionEnd > aStart)
|
||||
aSelectionEnd = newEnd;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Optional<nsAString> direction;
|
||||
SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv);
|
||||
}
|
||||
|
||||
int32_t
|
||||
HTMLInputElement::GetSelectionStart(ErrorResult& aRv)
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsIConstraintValidation.h"
|
||||
#include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit()
|
||||
#include "mozilla/dom/HTMLInputElementBinding.h"
|
||||
#include "nsIFilePicker.h"
|
||||
#include "nsIContentPrefService2.h"
|
||||
#include "mozilla/Decimal.h"
|
||||
@ -641,6 +642,13 @@ public:
|
||||
const Optional< nsAString >& direction,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv);
|
||||
|
||||
void SetRangeText(const nsAString& aReplacement, uint32_t aStart,
|
||||
uint32_t aEnd, const SelectionMode& aSelectMode,
|
||||
ErrorResult& aRv, int32_t aSelectionStart = -1,
|
||||
int32_t aSelectionEnd = -1);
|
||||
|
||||
// XPCOM GetAlign() is OK
|
||||
void SetAlign(const nsAString& aValue, ErrorResult& aRv)
|
||||
{
|
||||
@ -1239,6 +1247,15 @@ private:
|
||||
return MayFireChangeOnBlur(mType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if setRangeText can be called on element
|
||||
*/
|
||||
bool SupportsSetRangeText() const {
|
||||
return mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_SEARCH ||
|
||||
mType == NS_FORM_INPUT_URL || mType == NS_FORM_INPUT_TEL ||
|
||||
mType == NS_FORM_INPUT_PASSWORD;
|
||||
}
|
||||
|
||||
static bool MayFireChangeOnBlur(uint8_t aType) {
|
||||
return IsSingleLineTextControl(false, aType) ||
|
||||
aType == NS_FORM_INPUT_RANGE;
|
||||
|
@ -5,6 +5,7 @@
|
||||
MOCHITEST_FILES = \
|
||||
save_restore_radio_groups.sjs \
|
||||
test_save_restore_radio_groups.html \
|
||||
test_set_range_text.html \
|
||||
test_change_event.html \
|
||||
test_mozistextfield.html \
|
||||
test_input_attributes_reflection.html \
|
||||
|
183
content/html/content/test/forms/test_set_range_text.html
Normal file
183
content/html/content/test/forms/test_set_range_text.html
Normal file
@ -0,0 +1,183 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=850364
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 850364</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=850364">Mozilla Bug 850364</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
<!-- "SetRangeText() supported types"-->
|
||||
<input type="text" id="input_text"></input>
|
||||
<input type="search" id="input_search"></input>
|
||||
<input type="url" id="input_url"></input>
|
||||
<input type="tel" id="input_tel"></input>
|
||||
<input type="password" id="input_password"></input>
|
||||
|
||||
<!-- "SetRangeText() non-supported types" -->
|
||||
<input type="button" id="input_button"></input>
|
||||
<input type="submit" id="input_submit"></input>
|
||||
<input type="image" id="input_image"></input>
|
||||
<input type="reset" id="input_reset"></input>
|
||||
<input type="radio" id="input_radio"></input>
|
||||
<input type="checkbox" id="input_checkbox"></input>
|
||||
<input type="range" id="input_range"></input>
|
||||
<input type="file" id="input_file"></input>
|
||||
<input type="email" id="input_email"></input>
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 850364 **/
|
||||
|
||||
var SupportedTypes = ["text", "search", "url", "tel", "password"];
|
||||
var NonSupportedTypes = ["button", "submit", "image", "reset", "radio",
|
||||
"checkbox", "range", "file", "email"];
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function TestInputs() {
|
||||
|
||||
var opThrows, elem, i, msg;
|
||||
|
||||
//Non-supported types should throw
|
||||
for (i = 0; i < NonSupportedTypes.length; ++i) {
|
||||
opThrows = false;
|
||||
msg = "input_" + NonSupportedTypes[i];
|
||||
elem = document.getElementById(msg);
|
||||
elem.focus();
|
||||
try {
|
||||
elem.setRangeText("abc");
|
||||
} catch (ex) {
|
||||
opThrows = true;
|
||||
}
|
||||
ok(opThrows, msg + " should throw NotSupportedError");
|
||||
}
|
||||
|
||||
//Supported types should not throw
|
||||
for (i = 0; i < SupportedTypes.length; ++i) {
|
||||
opThrows = false;
|
||||
msg = "input_" + SupportedTypes[i];
|
||||
elem = document.getElementById(msg);
|
||||
elem.focus();
|
||||
try {
|
||||
elem.setRangeText("abc");
|
||||
} catch (ex) {
|
||||
opThrows = true;
|
||||
}
|
||||
is(opThrows, false, msg + " should not throw NotSupportedError");
|
||||
|
||||
elem.addEventListener("select", function (aEvent) {
|
||||
ok(true, "select event should be fired for "+ msg);
|
||||
}, false);
|
||||
|
||||
elem.addEventListener("input", function (aEvent) {
|
||||
ok(false, "input event should NOT be fired for " + msg);
|
||||
}, false);
|
||||
|
||||
//test setRange(replacement)
|
||||
elem.value = "0123456789ABCDEF";
|
||||
elem.setSelectionRange(1, 6);
|
||||
elem.setRangeText("xyz");
|
||||
is(elem.value, "0xyz6789ABCDEF", msg + ".value == \"0xyz6789ABCDEF\"");
|
||||
is(elem.selectionStart, 1, msg + ".selectionStart == 1");
|
||||
is(elem.selectionEnd, 4, msg + ".selectionEnd == 4");
|
||||
elem.setRangeText("mnk");
|
||||
is(elem.value, "0mnk6789ABCDEF", msg + ".value == \"0mnk6789ABCDEF\"");
|
||||
|
||||
//test SetRange(replacement, start, end, mode) with start > end
|
||||
try {
|
||||
elem.setRangeText("abc", 20, 4);
|
||||
} catch (ex) {
|
||||
opThrows = (ex.name == "IndexSizeError" && ex.code == DOMException.INDEX_SIZE_ERR);
|
||||
}
|
||||
is(opThrows, true, msg + " should throw IndexSizeError");
|
||||
|
||||
//test SelectionMode 'select'
|
||||
elem.value = "0123456789ABCDEF";
|
||||
elem.setRangeText("xyz", 4, 9, "select");
|
||||
is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
|
||||
is(elem.selectionStart, 4, msg + ".selectionStart == 4, with \"select\"");
|
||||
is(elem.selectionEnd, 7, msg + ".selectionEnd == 7, with \"select\"");
|
||||
|
||||
elem.setRangeText("pqm", 6, 25, "select");
|
||||
is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
|
||||
is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"select\"");
|
||||
is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"select\"");
|
||||
|
||||
//test SelectionMode 'start'
|
||||
elem.value = "0123456789ABCDEF";
|
||||
elem.setRangeText("xyz", 4, 9, "start");
|
||||
is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
|
||||
is(elem.selectionStart, 4, msg + ".selectionStart == 4, with \"start\"");
|
||||
is(elem.selectionEnd, 4, msg + ".selectionEnd == 4, with \"start\"");
|
||||
|
||||
elem.setRangeText("pqm", 6, 25, "start");
|
||||
is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
|
||||
is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"start\"");
|
||||
is(elem.selectionEnd, 6, msg + ".selectionEnd == 6, with \"start\"");
|
||||
|
||||
//test SelectionMode 'end'
|
||||
elem.value = "0123456789ABCDEF";
|
||||
elem.setRangeText("xyz", 4, 9, "end");
|
||||
is(elem.value, "0123xyz9ABCDEF", msg + ".value == \"0123xyz9ABCDEF\"");
|
||||
is(elem.selectionStart, 7, msg + ".selectionStart == 7, with \"end\"");
|
||||
is(elem.selectionEnd, 7, msg + ".selectionEnd == 7, with \"end\"");
|
||||
|
||||
elem.setRangeText("pqm", 6, 25, "end");
|
||||
is(elem.value, "0123xypqm", msg + ".value == \"0123xypqm\"");
|
||||
is(elem.selectionStart, 9, msg + ".selectionStart == 9, with \"end\"");
|
||||
is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"end\"");
|
||||
|
||||
//test SelectionMode 'preserve' (default)
|
||||
|
||||
//subcase: selection{Start|End} > end
|
||||
elem.value = "0123456789";
|
||||
elem.setSelectionRange(6, 9);
|
||||
elem.setRangeText("Z", 1, 2, "preserve");
|
||||
is(elem.value, "0Z23456789", msg + ".value == \"0Z23456789\"");
|
||||
is(elem.selectionStart, 6, msg + ".selectionStart == 6, with \"preserve\"");
|
||||
is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"preserve\"");
|
||||
|
||||
//subcase: selection{Start|End} < end
|
||||
elem.value = "0123456789";
|
||||
elem.setSelectionRange(4, 5);
|
||||
elem.setRangeText("QRST", 2, 9, "preserve");
|
||||
is(elem.value, "01QRST9", msg + ".value == \"01QRST9\"");
|
||||
is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"preserve\"");
|
||||
is(elem.selectionEnd, 6, msg + ".selectionEnd == 6, with \"preserve\"");
|
||||
|
||||
//subcase: selectionStart > end, selectionEnd < end
|
||||
elem.value = "0123456789";
|
||||
elem.setSelectionRange(8, 4);
|
||||
elem.setRangeText("QRST", 1, 5);
|
||||
is(elem.value, "0QRST56789", msg + ".value == \"0QRST56789\"");
|
||||
is(elem.selectionStart, 1, msg + ".selectionStart == 1, with \"default\"");
|
||||
is(elem.selectionEnd, 5, msg + ".selectionEnd == 5, with \"default\"");
|
||||
|
||||
//subcase: selectionStart < end, selectionEnd > end
|
||||
elem.value = "0123456789";
|
||||
elem.setSelectionRange(4, 9);
|
||||
elem.setRangeText("QRST", 2, 6);
|
||||
is(elem.value, "01QRST6789", msg + ".value == \"01QRST6789\"");
|
||||
is(elem.selectionStart, 2, msg + ".selectionStart == 2, with \"default\"");
|
||||
is(elem.selectionEnd, 9, msg + ".selectionEnd == 9, with \"default\"");
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
addLoadEvent(TestInputs);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -1,16 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(srcdir)/../../content/src \
|
||||
-I$(topsrcdir)/layout/style \
|
||||
-I$(topsrcdir)/dom/base \
|
||||
-I$(topsrcdir)/caps/include \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
$(NULL)
|
@ -33,3 +33,12 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../content/src',
|
||||
'/caps/include',
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/dom/base',
|
||||
'/layout/style',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -125,6 +125,19 @@ BufferComplexMultiply(const float* aInput,
|
||||
}
|
||||
}
|
||||
|
||||
float
|
||||
AudioBufferPeakValue(const float *aInput, uint32_t aSize)
|
||||
{
|
||||
float max = 0.0f;
|
||||
for (uint32_t i = 0; i < aSize; i++) {
|
||||
float mag = fabs(aInput[i]);
|
||||
if (mag > max) {
|
||||
max = mag;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
void
|
||||
AudioBlockCopyChannelWithScale(const float aInput[WEBAUDIO_BLOCK_SIZE],
|
||||
const float aScale[WEBAUDIO_BLOCK_SIZE],
|
||||
|
@ -134,6 +134,11 @@ void BufferComplexMultiply(const float* aInput,
|
||||
float* aOutput,
|
||||
uint32_t aSize);
|
||||
|
||||
/**
|
||||
* Vector maximum element magnitude ( max(abs(aInput)) ).
|
||||
*/
|
||||
float AudioBufferPeakValue(const float* aInput, uint32_t aSize);
|
||||
|
||||
/**
|
||||
* In place gain. aScale == 1.0f should be optimized.
|
||||
*/
|
||||
|
@ -1,3 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
@ -1,10 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../../base/src \
|
||||
-I$(srcdir)/../../html/content/src \
|
||||
$(NULL)
|
@ -25,3 +25,7 @@ LIBRARY_NAME = 'gkconmediaplugins_s'
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/html/content/src',
|
||||
]
|
||||
|
@ -1,11 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../../base/src \
|
||||
-I$(srcdir)/../../html/content/src \
|
||||
$(NULL)
|
@ -23,3 +23,7 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/html/content/src',
|
||||
]
|
||||
|
@ -396,8 +396,11 @@ AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
|
||||
}
|
||||
|
||||
nsRefPtr<PeriodicWave> periodicWave =
|
||||
new PeriodicWave(this, aRealData.Data(), aRealData.Length(),
|
||||
aImagData.Data(), aImagData.Length());
|
||||
new PeriodicWave(this, aRealData.Data(), aImagData.Data(),
|
||||
aImagData.Length(), aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
return periodicWave.forget();
|
||||
}
|
||||
|
||||
|
@ -39,11 +39,14 @@ public:
|
||||
CreateInterpolatedBlock(const FFTBlock& block0,
|
||||
const FFTBlock& block1, double interp);
|
||||
|
||||
// Transform FFTSize() points of aData and store the result internally.
|
||||
void PerformFFT(const float* aData)
|
||||
{
|
||||
EnsureFFT();
|
||||
kiss_fftr(mFFT, aData, mOutputBuffer.Elements());
|
||||
}
|
||||
// Inverse-transform internal data and store the resulting FFTSize()
|
||||
// points in aData.
|
||||
void PerformInverseFFT(float* aData)
|
||||
{
|
||||
EnsureIFFT();
|
||||
@ -52,6 +55,27 @@ public:
|
||||
aData[i] /= mFFTSize;
|
||||
}
|
||||
}
|
||||
// Inverse-transform the FFTSize()/2+1 points of data in each
|
||||
// of aRealDataIn and aImagDataIn and store the resulting
|
||||
// FFTSize() points in aRealDataOut.
|
||||
void PerformInverseFFT(float* aRealDataIn,
|
||||
float *aImagDataIn,
|
||||
float *aRealDataOut)
|
||||
{
|
||||
EnsureIFFT();
|
||||
const uint32_t inputSize = mFFTSize / 2 + 1;
|
||||
nsTArray<kiss_fft_cpx> inputBuffer;
|
||||
inputBuffer.SetLength(inputSize);
|
||||
for (uint32_t i = 0; i < inputSize; ++i) {
|
||||
inputBuffer[i].r = aRealDataIn[i];
|
||||
inputBuffer[i].i = aImagDataIn[i];
|
||||
}
|
||||
kiss_fftri(mIFFT, inputBuffer.Elements(), aRealDataOut);
|
||||
for (uint32_t i = 0; i < mFFTSize; ++i) {
|
||||
aRealDataOut[i] /= mFFTSize;
|
||||
}
|
||||
}
|
||||
|
||||
void Multiply(const FFTBlock& aFrame)
|
||||
{
|
||||
BufferComplexMultiply(reinterpret_cast<const float*>(mOutputBuffer.Elements()),
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "AudioNodeStream.h"
|
||||
#include "AudioDestinationNode.h"
|
||||
#include "WebAudioUtils.h"
|
||||
#include "blink/PeriodicWave.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
@ -36,6 +37,39 @@ NS_INTERFACE_MAP_END_INHERITING(AudioNode)
|
||||
NS_IMPL_ADDREF_INHERITED(OscillatorNode, AudioNode)
|
||||
NS_IMPL_RELEASE_INHERITED(OscillatorNode, AudioNode)
|
||||
|
||||
static const float sLeak = 0.995f;
|
||||
|
||||
class DCBlocker
|
||||
{
|
||||
public:
|
||||
// These are sane defauts when the initial mPhase is zero
|
||||
DCBlocker(float aLastInput = 0.0f,
|
||||
float aLastOutput = 0.0f,
|
||||
float aPole = 0.995)
|
||||
:mLastInput(aLastInput),
|
||||
mLastOutput(aLastOutput),
|
||||
mPole(aPole)
|
||||
{
|
||||
MOZ_ASSERT(aPole > 0);
|
||||
}
|
||||
|
||||
inline float Process(float aInput)
|
||||
{
|
||||
float out;
|
||||
|
||||
out = mLastOutput * mPole + aInput - mLastInput;
|
||||
mLastOutput = out;
|
||||
mLastInput = aInput;
|
||||
|
||||
return out;
|
||||
}
|
||||
private:
|
||||
float mLastInput;
|
||||
float mLastOutput;
|
||||
float mPole;
|
||||
};
|
||||
|
||||
|
||||
class OscillatorNodeEngine : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
@ -50,6 +84,17 @@ public:
|
||||
, mDetune(0.f)
|
||||
, mType(OscillatorType::Sine)
|
||||
, mPhase(0.)
|
||||
, mFinalFrequency(0.0)
|
||||
, mNumberOfHarmonics(0)
|
||||
, mSignalPeriod(0.0)
|
||||
, mAmplitudeAtZero(0.0)
|
||||
, mPhaseIncrement(0.0)
|
||||
, mSquare(0.0)
|
||||
, mTriangle(0.0)
|
||||
, mSaw(0.0)
|
||||
, mPhaseWrap(0.0)
|
||||
, mRecomputeFrequency(true)
|
||||
, mCustomLength(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -70,6 +115,7 @@ public:
|
||||
const AudioParamTimeline& aValue,
|
||||
TrackRate aSampleRate) MOZ_OVERRIDE
|
||||
{
|
||||
mRecomputeFrequency = true;
|
||||
switch (aIndex) {
|
||||
case FREQUENCY:
|
||||
MOZ_ASSERT(mSource && mDestination);
|
||||
@ -85,6 +131,7 @@ public:
|
||||
NS_ERROR("Bad OscillatorNodeEngine TimelineParameter");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetStreamTimeParameter(uint32_t aIndex, TrackTicks aParam)
|
||||
{
|
||||
switch (aIndex) {
|
||||
@ -94,29 +141,127 @@ public:
|
||||
NS_ERROR("Bad OscillatorNodeEngine StreamTimeParameter");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void SetInt32Parameter(uint32_t aIndex, int32_t aParam)
|
||||
{
|
||||
switch (aIndex) {
|
||||
case TYPE: mType = static_cast<OscillatorType>(aParam); break;
|
||||
default:
|
||||
NS_ERROR("Bad OscillatorNodeEngine Int32Parameter");
|
||||
case TYPE:
|
||||
// Set the new type.
|
||||
mType = static_cast<OscillatorType>(aParam);
|
||||
if (mType != OscillatorType::Custom) {
|
||||
// Forget any previous custom data.
|
||||
mCustomLength = 0;
|
||||
mCustom = nullptr;
|
||||
mPeriodicWave = nullptr;
|
||||
}
|
||||
// Update BLIT integrators with the new initial conditions.
|
||||
switch (mType) {
|
||||
case OscillatorType::Sine:
|
||||
mPhase = 0.0;
|
||||
break;
|
||||
case OscillatorType::Square:
|
||||
mPhase = 0.0;
|
||||
// Initial integration condition is -0.5, because our
|
||||
// square has 50% duty cycle.
|
||||
mSquare = -0.5;
|
||||
break;
|
||||
case OscillatorType::Triangle:
|
||||
// Initial mPhase and related integration condition so the
|
||||
// triangle is in the middle of the first upward slope.
|
||||
// XXX actually do the maths and put the right number here.
|
||||
mPhase = (float)(M_PI / 2);
|
||||
mSquare = 0.5;
|
||||
mTriangle = 0.0;
|
||||
break;
|
||||
case OscillatorType::Sawtooth:
|
||||
// Initial mPhase so the oscillator starts at the
|
||||
// middle of the ramp, per spec.
|
||||
mPhase = (float)(M_PI / 2);
|
||||
// mSaw = 0 when mPhase = pi/2.
|
||||
mSaw = 0.0;
|
||||
break;
|
||||
case OscillatorType::Custom:
|
||||
// Custom waveforms don't use BLIT.
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Bad OscillatorNodeEngine type parameter.");
|
||||
}
|
||||
// End type switch.
|
||||
break;
|
||||
case PERIODICWAVE:
|
||||
MOZ_ASSERT(aParam >= 0, "negative custom array length");
|
||||
mCustomLength = static_cast<uint32_t>(aParam);
|
||||
break;
|
||||
default:
|
||||
NS_ERROR("Bad OscillatorNodeEngine Int32Parameter.");
|
||||
}
|
||||
// End index switch.
|
||||
}
|
||||
|
||||
virtual void SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList> aBuffer)
|
||||
{
|
||||
MOZ_ASSERT(mCustomLength, "Custom buffer sent before length");
|
||||
mCustom = aBuffer;
|
||||
MOZ_ASSERT(mCustom->GetChannels() == 2,
|
||||
"PeriodicWave should have sent two channels");
|
||||
mPeriodicWave = WebCore::PeriodicWave::create(mSource->SampleRate(),
|
||||
mCustom->GetData(0), mCustom->GetData(1), mCustomLength);
|
||||
}
|
||||
|
||||
void IncrementPhase()
|
||||
{
|
||||
mPhase += mPhaseIncrement;
|
||||
if (mPhase > mPhaseWrap) {
|
||||
mPhase -= mPhaseWrap;
|
||||
}
|
||||
}
|
||||
|
||||
double ComputeFrequency(TrackTicks ticks, size_t count)
|
||||
// Square and triangle are using a bipolar band-limited impulse train, saw is
|
||||
// using a normal band-limited impulse train.
|
||||
bool UsesBipolarBLIT() {
|
||||
return mType == OscillatorType::Square || mType == OscillatorType::Triangle;
|
||||
}
|
||||
|
||||
void UpdateFrequencyIfNeeded(TrackTicks ticks, size_t count)
|
||||
{
|
||||
double frequency, detune;
|
||||
if (mFrequency.HasSimpleValue()) {
|
||||
|
||||
bool simpleFrequency = mFrequency.HasSimpleValue();
|
||||
bool simpleDetune = mDetune.HasSimpleValue();
|
||||
|
||||
// Shortcut if frequency-related AudioParam are not automated, and we
|
||||
// already have computed the frequency information and related parameters.
|
||||
if (simpleFrequency && simpleDetune && !mRecomputeFrequency) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (simpleFrequency) {
|
||||
frequency = mFrequency.GetValue();
|
||||
} else {
|
||||
frequency = mFrequency.GetValueAtTime(ticks, count);
|
||||
}
|
||||
if (mDetune.HasSimpleValue()) {
|
||||
if (simpleDetune) {
|
||||
detune = mDetune.GetValue();
|
||||
} else {
|
||||
detune = mDetune.GetValueAtTime(ticks, count);
|
||||
}
|
||||
return frequency * pow(2., detune / 1200.);
|
||||
|
||||
mFinalFrequency = frequency * pow(2., detune / 1200.);
|
||||
mRecomputeFrequency = false;
|
||||
|
||||
// When using bipolar BLIT, we divide the signal period by two, because we
|
||||
// are using two BLIT out of phase.
|
||||
mSignalPeriod = UsesBipolarBLIT() ? 0.5 * mSource->SampleRate() / mFinalFrequency
|
||||
: mSource->SampleRate() / mFinalFrequency;
|
||||
// Wrap the phase accordingly:
|
||||
mPhaseWrap = UsesBipolarBLIT() || mType == OscillatorType::Sine ? 2 * M_PI
|
||||
: M_PI;
|
||||
// Even number of harmonics for bipolar blit, odd otherwise.
|
||||
mNumberOfHarmonics = UsesBipolarBLIT() ? 2 * floor(0.5 * mSignalPeriod)
|
||||
: 2 * floor(0.5 * mSignalPeriod) + 1;
|
||||
mPhaseIncrement = mType == OscillatorType::Sine ? 2 * M_PI / mSignalPeriod
|
||||
: M_PI / mSignalPeriod;
|
||||
mAmplitudeAtZero = mNumberOfHarmonics / mSignalPeriod;
|
||||
}
|
||||
|
||||
void FillBounds(float* output, TrackTicks ticks,
|
||||
@ -141,95 +286,138 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeSine(AudioChunk *aOutput)
|
||||
float BipolarBLIT()
|
||||
{
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
|
||||
float blit;
|
||||
float denom = sin(mPhase);
|
||||
|
||||
TrackTicks ticks = mSource->GetCurrentPosition();
|
||||
uint32_t start, end;
|
||||
FillBounds(output, ticks, start, end);
|
||||
|
||||
double rate = 2.*M_PI / mSource->SampleRate();
|
||||
double phase = mPhase;
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
phase += ComputeFrequency(ticks, i) * rate;
|
||||
output[i] = sin(phase);
|
||||
}
|
||||
mPhase = phase;
|
||||
while (mPhase > 2.0*M_PI) {
|
||||
// Rescale to avoid precision reductions on long runs.
|
||||
mPhase -= 2.0*M_PI;
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeSquare(AudioChunk *aOutput)
|
||||
{
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
|
||||
|
||||
TrackTicks ticks = mSource->GetCurrentPosition();
|
||||
uint32_t start, end;
|
||||
FillBounds(output, ticks, start, end);
|
||||
|
||||
double rate = 1.0 / mSource->SampleRate();
|
||||
double phase = mPhase;
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
phase += ComputeFrequency(ticks, i) * rate;
|
||||
if (phase > 1.0) {
|
||||
phase -= 1.0;
|
||||
}
|
||||
output[i] = phase < 0.5 ? 1.0 : -1.0;
|
||||
}
|
||||
mPhase = phase;
|
||||
}
|
||||
|
||||
void ComputeSawtooth(AudioChunk *aOutput)
|
||||
{
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
|
||||
|
||||
TrackTicks ticks = mSource->GetCurrentPosition();
|
||||
uint32_t start, end;
|
||||
FillBounds(output, ticks, start, end);
|
||||
|
||||
double rate = 1.0 / mSource->SampleRate();
|
||||
double phase = mPhase;
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
phase += ComputeFrequency(ticks, i) * rate;
|
||||
if (phase > 1.0) {
|
||||
phase -= 1.0;
|
||||
}
|
||||
output[i] = phase < 0.5 ? 2.0*phase : 2.0*(phase - 1.0);
|
||||
}
|
||||
mPhase = phase;
|
||||
}
|
||||
|
||||
void ComputeTriangle(AudioChunk *aOutput)
|
||||
{
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
|
||||
|
||||
TrackTicks ticks = mSource->GetCurrentPosition();
|
||||
uint32_t start, end;
|
||||
FillBounds(output, ticks, start, end);
|
||||
|
||||
double rate = 1.0 / mSource->SampleRate();
|
||||
double phase = mPhase;
|
||||
for (uint32_t i = start; i < end; ++i) {
|
||||
phase += ComputeFrequency(ticks, i) * rate;
|
||||
if (phase > 1.0) {
|
||||
phase -= 1.0;
|
||||
}
|
||||
if (phase < 0.25) {
|
||||
output[i] = 4.0*phase;
|
||||
} else if (phase < 0.75) {
|
||||
output[i] = 1.0 - 4.0*(phase - 0.25);
|
||||
if (fabs(denom) < std::numeric_limits<float>::epsilon()) {
|
||||
if (mPhase < 0.1f || mPhase > 2 * M_PI - 0.1f) {
|
||||
blit = mAmplitudeAtZero;
|
||||
} else {
|
||||
output[i] = 4.0*(phase - 0.75) - 1.0;
|
||||
blit = -mAmplitudeAtZero;
|
||||
}
|
||||
} else {
|
||||
blit = sin(mNumberOfHarmonics * mPhase);
|
||||
blit /= mSignalPeriod * denom;
|
||||
}
|
||||
return blit;
|
||||
}
|
||||
|
||||
float UnipolarBLIT()
|
||||
{
|
||||
float blit;
|
||||
float denom = sin(mPhase);
|
||||
|
||||
if (fabs(denom) <= std::numeric_limits<float>::epsilon()) {
|
||||
blit = mAmplitudeAtZero;
|
||||
} else {
|
||||
blit = sin(mNumberOfHarmonics * mPhase);
|
||||
blit /= mSignalPeriod * denom;
|
||||
}
|
||||
|
||||
return blit;
|
||||
}
|
||||
|
||||
void ComputeSine(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
||||
{
|
||||
for (uint32_t i = aStart; i < aEnd; ++i) {
|
||||
UpdateFrequencyIfNeeded(ticks, i);
|
||||
|
||||
aOutput[i] = sin(mPhase);
|
||||
|
||||
IncrementPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeSquare(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
||||
{
|
||||
for (uint32_t i = aStart; i < aEnd; ++i) {
|
||||
UpdateFrequencyIfNeeded(ticks, i);
|
||||
// Integration to get us a square. It turns out we can have a
|
||||
// pure integrator here.
|
||||
mSquare += BipolarBLIT();
|
||||
aOutput[i] = mSquare;
|
||||
// maybe we want to apply a gain, the wg has not decided yet
|
||||
aOutput[i] *= 1.5;
|
||||
IncrementPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeSawtooth(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
||||
{
|
||||
float dcoffset;
|
||||
for (uint32_t i = aStart; i < aEnd; ++i) {
|
||||
UpdateFrequencyIfNeeded(ticks, i);
|
||||
// DC offset so the Saw does not ramp up to infinity when integrating.
|
||||
dcoffset = mFinalFrequency / mSource->SampleRate();
|
||||
// Integrate and offset so we get mAmplitudeAtZero sawtooth. We have a
|
||||
// very low frequency component somewhere here, but I'm not sure where.
|
||||
mSaw += UnipolarBLIT() - dcoffset;
|
||||
// reverse the saw so we are spec compliant
|
||||
aOutput[i] = -mSaw * 1.5;
|
||||
|
||||
IncrementPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeTriangle(float * aOutput, TrackTicks ticks, uint32_t aStart, uint32_t aEnd)
|
||||
{
|
||||
for (uint32_t i = aStart; i < aEnd; ++i) {
|
||||
UpdateFrequencyIfNeeded(ticks, i);
|
||||
// Integrate to get a square
|
||||
mSquare += BipolarBLIT();
|
||||
// Leaky integrate to get a triangle. We get too much dc offset if we don't
|
||||
// leaky integrate here.
|
||||
// C6 = k0 / period
|
||||
// (period is samplingrate / frequency, k0 = (PI/2)/(2*PI)) = 0.25
|
||||
float C6 = 0.25 / (mSource->SampleRate() / mFinalFrequency);
|
||||
mTriangle = mTriangle * sLeak + mSquare + C6;
|
||||
// DC Block, and scale back to [-1.0; 1.0]
|
||||
aOutput[i] = mDCBlocker.Process(mTriangle) / (mSignalPeriod/2) * 1.5;
|
||||
|
||||
IncrementPhase();
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeCustom(float* aOutput,
|
||||
TrackTicks ticks,
|
||||
uint32_t aStart,
|
||||
uint32_t aEnd)
|
||||
{
|
||||
MOZ_ASSERT(mPeriodicWave, "No custom waveform data");
|
||||
|
||||
uint32_t periodicWaveSize = mPeriodicWave->periodicWaveSize();
|
||||
float* higherWaveData = nullptr;
|
||||
float* lowerWaveData = nullptr;
|
||||
float tableInterpolationFactor;
|
||||
float rate = 1.0 / mSource->SampleRate();
|
||||
|
||||
for (uint32_t i = aStart; i < aEnd; ++i) {
|
||||
UpdateFrequencyIfNeeded(ticks, i);
|
||||
mPeriodicWave->waveDataForFundamentalFrequency(mFinalFrequency,
|
||||
lowerWaveData,
|
||||
higherWaveData,
|
||||
tableInterpolationFactor);
|
||||
// mPhase runs 0..periodicWaveSize here instead of 0..2*M_PI.
|
||||
mPhase += periodicWaveSize * mFinalFrequency * rate;
|
||||
if (mPhase >= periodicWaveSize) {
|
||||
mPhase -= periodicWaveSize;
|
||||
}
|
||||
// Bilinear interpolation between adjacent samples in each table.
|
||||
uint32_t j1 = floor(mPhase);
|
||||
uint32_t j2 = j1 + 1;
|
||||
if (j2 >= periodicWaveSize) {
|
||||
j2 -= periodicWaveSize;
|
||||
}
|
||||
float sampleInterpolationFactor = mPhase - j1;
|
||||
float lower = sampleInterpolationFactor * lowerWaveData[j1] +
|
||||
(1 - sampleInterpolationFactor) * lowerWaveData[j2];
|
||||
float higher = sampleInterpolationFactor * higherWaveData[j1] +
|
||||
(1 - sampleInterpolationFactor) * higherWaveData[j2];
|
||||
aOutput[i] = tableInterpolationFactor * lower +
|
||||
(1 - tableInterpolationFactor) * higher;
|
||||
}
|
||||
mPhase = phase;
|
||||
}
|
||||
|
||||
void ComputeSilence(AudioChunk *aOutput)
|
||||
@ -261,25 +449,38 @@ public:
|
||||
*aFinished = true;
|
||||
return;
|
||||
}
|
||||
|
||||
AllocateAudioBlock(1, aOutput);
|
||||
float* output = static_cast<float*>(
|
||||
const_cast<void*>(aOutput->mChannelData[0]));
|
||||
|
||||
uint32_t start, end;
|
||||
FillBounds(output, ticks, start, end);
|
||||
|
||||
// Synthesize the correct waveform.
|
||||
switch (mType) {
|
||||
switch(mType) {
|
||||
case OscillatorType::Sine:
|
||||
ComputeSine(aOutput);
|
||||
ComputeSine(output, ticks, start, end);
|
||||
break;
|
||||
case OscillatorType::Square:
|
||||
ComputeSquare(aOutput);
|
||||
break;
|
||||
case OscillatorType::Sawtooth:
|
||||
ComputeSawtooth(aOutput);
|
||||
ComputeSquare(output, ticks, start, end);
|
||||
break;
|
||||
case OscillatorType::Triangle:
|
||||
ComputeTriangle(aOutput);
|
||||
ComputeTriangle(output, ticks, start, end);
|
||||
break;
|
||||
case OscillatorType::Sawtooth:
|
||||
ComputeSawtooth(output, ticks, start, end);
|
||||
break;
|
||||
case OscillatorType::Custom:
|
||||
ComputeCustom(output, ticks, start, end);
|
||||
break;
|
||||
default:
|
||||
ComputeSilence(aOutput);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
DCBlocker mDCBlocker;
|
||||
AudioNodeStream* mSource;
|
||||
AudioNodeStream* mDestination;
|
||||
TrackTicks mStart;
|
||||
@ -287,7 +488,20 @@ public:
|
||||
AudioParamTimeline mFrequency;
|
||||
AudioParamTimeline mDetune;
|
||||
OscillatorType mType;
|
||||
double mPhase;
|
||||
float mPhase;
|
||||
float mFinalFrequency;
|
||||
uint32_t mNumberOfHarmonics;
|
||||
float mSignalPeriod;
|
||||
float mAmplitudeAtZero;
|
||||
float mPhaseIncrement;
|
||||
float mSquare;
|
||||
float mTriangle;
|
||||
float mSaw;
|
||||
float mPhaseWrap;
|
||||
bool mRecomputeFrequency;
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> mCustom;
|
||||
uint32_t mCustomLength;
|
||||
nsAutoPtr<WebCore::PeriodicWave> mPeriodicWave;
|
||||
};
|
||||
|
||||
OscillatorNode::OscillatorNode(AudioContext* aContext)
|
||||
@ -338,10 +552,25 @@ OscillatorNode::SendDetuneToStream(AudioNode* aNode)
|
||||
void
|
||||
OscillatorNode::SendTypeToStream()
|
||||
{
|
||||
SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
|
||||
if (mType == OscillatorType::Custom) {
|
||||
// TODO: Send the custom wave table somehow
|
||||
// The engine assumes we'll send the custom data before updating the type.
|
||||
SendPeriodicWaveToStream();
|
||||
}
|
||||
SendInt32ParameterToStream(OscillatorNodeEngine::TYPE, static_cast<int32_t>(mType));
|
||||
}
|
||||
|
||||
void OscillatorNode::SendPeriodicWaveToStream()
|
||||
{
|
||||
NS_ASSERTION(mType == OscillatorType::Custom,
|
||||
"Sending custom waveform to engine thread with non-custom type");
|
||||
AudioNodeStream* ns = static_cast<AudioNodeStream*>(mStream.get());
|
||||
MOZ_ASSERT(ns, "Missing node stream.");
|
||||
MOZ_ASSERT(mPeriodicWave, "Send called without PeriodicWave object.");
|
||||
SendInt32ParameterToStream(OscillatorNodeEngine::PERIODICWAVE,
|
||||
mPeriodicWave->DataLength());
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> data =
|
||||
mPeriodicWave->GetThreadSharedBuffer();
|
||||
ns->SetBuffer(data.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -77,9 +77,10 @@ public:
|
||||
// Shut up the compiler warning
|
||||
break;
|
||||
}
|
||||
|
||||
if (aType == OscillatorType::Custom) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
// ::Custom can only be set by setPeriodicWave().
|
||||
// https://github.com/WebAudio/web-audio-api/issues/105 for exception.
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
mType = aType;
|
||||
@ -108,6 +109,7 @@ public:
|
||||
void SetPeriodicWave(PeriodicWave& aPeriodicWave)
|
||||
{
|
||||
mPeriodicWave = &aPeriodicWave;
|
||||
// SendTypeToStream will call SendPeriodicWaveToStream for us.
|
||||
mType = OscillatorType::Custom;
|
||||
SendTypeToStream();
|
||||
}
|
||||
@ -120,6 +122,7 @@ private:
|
||||
static void SendFrequencyToStream(AudioNode* aNode);
|
||||
static void SendDetuneToStream(AudioNode* aNode);
|
||||
void SendTypeToStream();
|
||||
void SendPeriodicWaveToStream();
|
||||
|
||||
private:
|
||||
OscillatorType mType;
|
||||
|
@ -18,13 +18,30 @@ NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PeriodicWave, Release)
|
||||
|
||||
PeriodicWave::PeriodicWave(AudioContext* aContext,
|
||||
const float* aRealData,
|
||||
uint32_t aRealDataLength,
|
||||
const float* aImagData,
|
||||
uint32_t aImagDataLength)
|
||||
const uint32_t aLength,
|
||||
ErrorResult& aRv)
|
||||
: mContext(aContext)
|
||||
{
|
||||
MOZ_ASSERT(aContext);
|
||||
SetIsDOMBinding();
|
||||
|
||||
// Caller should have checked this and thrown.
|
||||
MOZ_ASSERT(aLength > 0);
|
||||
MOZ_ASSERT(aLength <= 4096);
|
||||
mLength = aLength;
|
||||
|
||||
// Copy coefficient data. The two arrays share an allocation.
|
||||
mCoefficients = new ThreadSharedFloatArrayBufferList(2);
|
||||
float* buffer = static_cast<float*>(malloc(aLength*sizeof(float)*2));
|
||||
if (buffer == nullptr) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
PodCopy(buffer, aRealData, aLength);
|
||||
mCoefficients->SetData(0, buffer, buffer);
|
||||
PodCopy(buffer+aLength, aImagData, aLength);
|
||||
mCoefficients->SetData(1, nullptr, buffer+aLength);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -33,6 +50,6 @@ PeriodicWave::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
return PeriodicWaveBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "EnableWebAudioCheck.h"
|
||||
#include "AudioContext.h"
|
||||
#include "AudioNodeEngine.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
@ -24,9 +25,9 @@ class PeriodicWave MOZ_FINAL : public nsWrapperCache,
|
||||
public:
|
||||
PeriodicWave(AudioContext* aContext,
|
||||
const float* aRealData,
|
||||
uint32_t aRealDataLength,
|
||||
const float* aImagData,
|
||||
uint32_t aImagDataLength);
|
||||
const uint32_t aLength,
|
||||
ErrorResult& aRv);
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PeriodicWave)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(PeriodicWave)
|
||||
@ -39,12 +40,23 @@ public:
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
uint32_t DataLength() const
|
||||
{
|
||||
return mLength;
|
||||
}
|
||||
|
||||
ThreadSharedFloatArrayBufferList* GetThreadSharedBuffer() const
|
||||
{
|
||||
return mCoefficients;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioContext> mContext;
|
||||
nsRefPtr<ThreadSharedFloatArrayBufferList> mCoefficients;
|
||||
uint32_t mLength;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
297
content/media/webaudio/blink/PeriodicWave.cpp
Normal file
297
content/media/webaudio/blink/PeriodicWave.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "PeriodicWave.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include "mozilla/FFTBlock.h"
|
||||
|
||||
const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
|
||||
const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
|
||||
const float CentsPerRange = 1200 / 3; // 1/3 Octave.
|
||||
|
||||
using namespace mozilla;
|
||||
using mozilla::dom::OscillatorType;
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
PeriodicWave* PeriodicWave::create(float sampleRate,
|
||||
const float* real,
|
||||
const float* imag,
|
||||
size_t numberOfComponents)
|
||||
{
|
||||
bool isGood = real && imag && numberOfComponents > 0 &&
|
||||
numberOfComponents <= PeriodicWaveSize;
|
||||
MOZ_ASSERT(isGood);
|
||||
if (isGood) {
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
|
||||
return periodicWave;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PeriodicWave* PeriodicWave::createSine(float sampleRate)
|
||||
{
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Sine);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PeriodicWave* PeriodicWave::createSquare(float sampleRate)
|
||||
{
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Square);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PeriodicWave* PeriodicWave::createSawtooth(float sampleRate)
|
||||
{
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PeriodicWave* PeriodicWave::createTriangle(float sampleRate)
|
||||
{
|
||||
PeriodicWave* periodicWave = new PeriodicWave(sampleRate);
|
||||
periodicWave->generateBasicWaveform(OscillatorType::Triangle);
|
||||
return periodicWave;
|
||||
}
|
||||
|
||||
PeriodicWave::PeriodicWave(float sampleRate)
|
||||
: m_sampleRate(sampleRate)
|
||||
, m_periodicWaveSize(PeriodicWaveSize)
|
||||
, m_numberOfRanges(NumberOfRanges)
|
||||
, m_centsPerRange(CentsPerRange)
|
||||
{
|
||||
float nyquist = 0.5 * m_sampleRate;
|
||||
m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
|
||||
m_rateScale = m_periodicWaveSize / m_sampleRate;
|
||||
}
|
||||
|
||||
void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
|
||||
{
|
||||
// Negative frequencies are allowed, in which case we alias
|
||||
// to the positive frequency.
|
||||
fundamentalFrequency = fabsf(fundamentalFrequency);
|
||||
|
||||
// Calculate the pitch range.
|
||||
float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
|
||||
float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
|
||||
|
||||
// Add one to round-up to the next range just in time to truncate
|
||||
// partials before aliasing occurs.
|
||||
float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
|
||||
|
||||
pitchRange = std::max(pitchRange, 0.0f);
|
||||
pitchRange = std::min(pitchRange, static_cast<float>(m_numberOfRanges - 1));
|
||||
|
||||
// The words "lower" and "higher" refer to the table data having
|
||||
// the lower and higher numbers of partials. It's a little confusing
|
||||
// since the range index gets larger the more partials we cull out.
|
||||
// So the lower table data will have a larger range index.
|
||||
unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
|
||||
unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
|
||||
|
||||
lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
|
||||
higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
|
||||
|
||||
// Ranges from 0 -> 1 to interpolate between lower -> higher.
|
||||
tableInterpolationFactor = pitchRange - rangeIndex1;
|
||||
}
|
||||
|
||||
unsigned PeriodicWave::maxNumberOfPartials() const
|
||||
{
|
||||
return m_periodicWaveSize / 2;
|
||||
}
|
||||
|
||||
unsigned PeriodicWave::numberOfPartialsForRange(unsigned rangeIndex) const
|
||||
{
|
||||
// Number of cents below nyquist where we cull partials.
|
||||
float centsToCull = rangeIndex * m_centsPerRange;
|
||||
|
||||
// A value from 0 -> 1 representing what fraction of the partials to keep.
|
||||
float cullingScale = pow(2, -centsToCull / 1200);
|
||||
|
||||
// The very top range will have all the partials culled.
|
||||
unsigned numberOfPartials = cullingScale * maxNumberOfPartials();
|
||||
|
||||
return numberOfPartials;
|
||||
}
|
||||
|
||||
// Convert into time-domain wave buffers.
|
||||
// One table is created for each range for non-aliasing playback
|
||||
// at different playback rates. Thus, higher ranges have more
|
||||
// high-frequency partials culled out.
|
||||
void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
|
||||
{
|
||||
float normalizationScale = 1;
|
||||
|
||||
unsigned fftSize = m_periodicWaveSize;
|
||||
unsigned halfSize = fftSize / 2 + 1;
|
||||
unsigned i;
|
||||
|
||||
numberOfComponents = std::min(numberOfComponents, halfSize);
|
||||
|
||||
m_bandLimitedTables.SetCapacity(m_numberOfRanges);
|
||||
|
||||
for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
|
||||
// This FFTBlock is used to cull partials (represented by frequency bins).
|
||||
FFTBlock frame(fftSize);
|
||||
float* realP = new float[halfSize];
|
||||
float* imagP = new float[halfSize];
|
||||
|
||||
// Copy from loaded frequency data and scale.
|
||||
float scale = fftSize;
|
||||
AudioBufferCopyWithScale(realData, scale, realP, numberOfComponents);
|
||||
AudioBufferCopyWithScale(imagData, scale, imagP, numberOfComponents);
|
||||
|
||||
// If fewer components were provided than 1/2 FFT size,
|
||||
// then clear the remaining bins.
|
||||
for (i = numberOfComponents; i < halfSize; ++i) {
|
||||
realP[i] = 0;
|
||||
imagP[i] = 0;
|
||||
}
|
||||
|
||||
// Generate complex conjugate because of the way the
|
||||
// inverse FFT is defined.
|
||||
float minusOne = -1;
|
||||
AudioBufferInPlaceScale(imagP, 1, minusOne, halfSize);
|
||||
|
||||
// Find the starting bin where we should start culling.
|
||||
// We need to clear out the highest frequencies to band-limit
|
||||
// the waveform.
|
||||
unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
|
||||
|
||||
// Cull the aliasing partials for this pitch range.
|
||||
for (i = numberOfPartials + 1; i < halfSize; ++i) {
|
||||
realP[i] = 0;
|
||||
imagP[i] = 0;
|
||||
}
|
||||
// Clear nyquist if necessary.
|
||||
if (numberOfPartials < halfSize)
|
||||
realP[halfSize-1] = 0;
|
||||
|
||||
// Clear any DC-offset.
|
||||
realP[0] = 0;
|
||||
|
||||
// Clear values which have no effect.
|
||||
imagP[0] = 0;
|
||||
imagP[halfSize-1] = 0;
|
||||
|
||||
// Create the band-limited table.
|
||||
AudioFloatArray* table = new AudioFloatArray(m_periodicWaveSize);
|
||||
m_bandLimitedTables.AppendElement(table);
|
||||
|
||||
// Apply an inverse FFT to generate the time-domain table data.
|
||||
float* data = m_bandLimitedTables[rangeIndex]->Elements();
|
||||
frame.PerformInverseFFT(realP, imagP, data);
|
||||
|
||||
// For the first range (which has the highest power), calculate
|
||||
// its peak value then compute normalization scale.
|
||||
if (!rangeIndex) {
|
||||
float maxValue;
|
||||
maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
|
||||
|
||||
if (maxValue)
|
||||
normalizationScale = 1.0f / maxValue;
|
||||
}
|
||||
|
||||
// Apply normalization scale.
|
||||
AudioBufferInPlaceScale(data, 1, normalizationScale, m_periodicWaveSize);
|
||||
}
|
||||
}
|
||||
|
||||
void PeriodicWave::generateBasicWaveform(OscillatorType shape)
|
||||
{
|
||||
const float piFloat = M_PI;
|
||||
unsigned fftSize = periodicWaveSize();
|
||||
unsigned halfSize = fftSize / 2 + 1;
|
||||
|
||||
AudioFloatArray real(halfSize);
|
||||
AudioFloatArray imag(halfSize);
|
||||
float* realP = real.Elements();
|
||||
float* imagP = imag.Elements();
|
||||
|
||||
// Clear DC and Nyquist.
|
||||
realP[0] = 0;
|
||||
imagP[0] = 0;
|
||||
realP[halfSize-1] = 0;
|
||||
imagP[halfSize-1] = 0;
|
||||
|
||||
for (unsigned n = 1; n < halfSize; ++n) {
|
||||
float omega = 2 * piFloat * n;
|
||||
float invOmega = 1 / omega;
|
||||
|
||||
// Fourier coefficients according to standard definition.
|
||||
float a; // Coefficient for cos().
|
||||
float b; // Coefficient for sin().
|
||||
|
||||
// Calculate Fourier coefficients depending on the shape.
|
||||
// Note that the overall scaling (magnitude) of the waveforms
|
||||
// is normalized in createBandLimitedTables().
|
||||
switch (shape) {
|
||||
case OscillatorType::Sine:
|
||||
// Standard sine wave function.
|
||||
a = 0;
|
||||
b = (n == 1) ? 1 : 0;
|
||||
break;
|
||||
case OscillatorType::Square:
|
||||
// Square-shaped waveform with the first half its maximum value
|
||||
// and the second half its minimum value.
|
||||
a = 0;
|
||||
b = invOmega * ((n & 1) ? 2 : 0);
|
||||
break;
|
||||
case OscillatorType::Sawtooth:
|
||||
// Sawtooth-shaped waveform with the first half ramping from
|
||||
// zero to maximum and the second half from minimum to zero.
|
||||
a = 0;
|
||||
b = -invOmega * cos(0.5 * omega);
|
||||
break;
|
||||
case OscillatorType::Triangle:
|
||||
// Triangle-shaped waveform going from its maximum value to
|
||||
// its minimum value then back to the maximum value.
|
||||
a = (4 - 4 * cos(0.5 * omega)) / (n * n * piFloat * piFloat);
|
||||
b = 0;
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("invalid oscillator type");
|
||||
a = 0;
|
||||
b = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
realP[n] = a;
|
||||
imagP[n] = b;
|
||||
}
|
||||
|
||||
createBandLimitedTables(realP, imagP, halfSize);
|
||||
}
|
||||
|
||||
} // namespace WebCore
|
103
content/media/webaudio/blink/PeriodicWave.h
Normal file
103
content/media/webaudio/blink/PeriodicWave.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Google Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
|
||||
* its contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef PeriodicWave_h
|
||||
#define PeriodicWave_h
|
||||
|
||||
#include "mozilla/dom/OscillatorNodeBinding.h"
|
||||
#include <nsAutoPtr.h>
|
||||
#include <nsTArray.h>
|
||||
|
||||
namespace WebCore {
|
||||
|
||||
typedef nsTArray<float> AudioFloatArray;
|
||||
|
||||
class PeriodicWave {
|
||||
public:
|
||||
static PeriodicWave* createSine(float sampleRate);
|
||||
static PeriodicWave* createSquare(float sampleRate);
|
||||
static PeriodicWave* createSawtooth(float sampleRate);
|
||||
static PeriodicWave* createTriangle(float sampleRate);
|
||||
|
||||
// Creates an arbitrary periodic wave given the frequency components
|
||||
// (Fourier coefficients).
|
||||
static PeriodicWave* create(float sampleRate,
|
||||
const float* real,
|
||||
const float* imag,
|
||||
size_t numberOfComponents);
|
||||
|
||||
// Returns pointers to the lower and higher wave data for the pitch range
|
||||
// containing the given fundamental frequency. These two tables are in
|
||||
// adjacent "pitch" ranges where the higher table will have the maximum
|
||||
// number of partials which won't alias when played back at this
|
||||
// fundamental frequency. The lower wave is the next range containing fewer
|
||||
// partials than the higher wave. Interpolation between these two tables
|
||||
// can be made according to tableInterpolationFactor. Where values
|
||||
// from 0 -> 1 interpolate between lower -> higher.
|
||||
void waveDataForFundamentalFrequency(float, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor);
|
||||
|
||||
// Returns the scalar multiplier to the oscillator frequency to calculate
|
||||
// wave buffer phase increment.
|
||||
float rateScale() const { return m_rateScale; }
|
||||
|
||||
unsigned periodicWaveSize() const { return m_periodicWaveSize; }
|
||||
float sampleRate() const { return m_sampleRate; }
|
||||
|
||||
private:
|
||||
explicit PeriodicWave(float sampleRate);
|
||||
|
||||
void generateBasicWaveform(mozilla::dom::OscillatorType);
|
||||
|
||||
float m_sampleRate;
|
||||
unsigned m_periodicWaveSize;
|
||||
unsigned m_numberOfRanges;
|
||||
float m_centsPerRange;
|
||||
|
||||
// The lowest frequency (in Hertz) where playback will include all of the
|
||||
// partials. Playing back lower than this frequency will gradually lose
|
||||
// more high-frequency information.
|
||||
// This frequency is quite low (~10Hz @ // 44.1KHz)
|
||||
float m_lowestFundamentalFrequency;
|
||||
|
||||
float m_rateScale;
|
||||
|
||||
unsigned numberOfRanges() const { return m_numberOfRanges; }
|
||||
|
||||
// Maximum possible number of partials (before culling).
|
||||
unsigned maxNumberOfPartials() const;
|
||||
|
||||
unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
|
||||
|
||||
// Creates tables based on numberOfComponents Fourier coefficients.
|
||||
void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents);
|
||||
nsTArray<nsAutoPtr<AudioFloatArray> > m_bandLimitedTables;
|
||||
};
|
||||
|
||||
} // namespace WebCore
|
||||
|
||||
#endif // PeriodicWave_h
|
@ -17,6 +17,7 @@ CPP_SOURCES += [
|
||||
'HRTFElevation.cpp',
|
||||
'HRTFKernel.cpp',
|
||||
'HRTFPanner.cpp',
|
||||
'PeriodicWave.cpp',
|
||||
'Reverb.cpp',
|
||||
'ReverbAccumulationBuffer.cpp',
|
||||
'ReverbConvolver.cpp',
|
||||
|
@ -22,10 +22,10 @@ addLoadEvent(function() {
|
||||
is(osc.type, "sine", "Correct default type");
|
||||
expectException(function() {
|
||||
osc.type = "custom";
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
}, DOMException.INVALID_STATE_ERR);
|
||||
expectException(function() {
|
||||
osc.type = osc.CUSTOM;
|
||||
}, DOMException.NOT_SUPPORTED_ERR);
|
||||
}, DOMException.INVALID_STATE_ERR);
|
||||
is(osc.type, "sine", "Cannot set the type to custom");
|
||||
is(osc.frequency.value, 440, "Correct default frequency value");
|
||||
is(osc.detune.value, 0, "Correct default detine value");
|
||||
@ -43,6 +43,12 @@ addLoadEvent(function() {
|
||||
osc.type = types[i];
|
||||
}
|
||||
|
||||
// Verify setPeriodicWave()
|
||||
var real = new Float32Array([1.0, 0.5, 0.25, 0.125]);
|
||||
var imag = new Float32Array([1.0, 0.7, -1.0, 0.5]);
|
||||
osc.setPeriodicWave(context.createPeriodicWave(real, imag));
|
||||
is(osc.type, "custom", "Failed to set custom waveform");
|
||||
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
||||
|
@ -1,10 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(srcdir)/../base/src \
|
||||
-I$(srcdir)/../../layout/style \
|
||||
-I$(srcdir)/../events/src \
|
||||
$(NULL)
|
@ -67,3 +67,8 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
LIBRARY_NAME = 'gkconsmil_s'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base/src',
|
||||
'../events/src',
|
||||
'/layout/style',
|
||||
]
|
||||
|
@ -1,17 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../../../xml/document/src \
|
||||
-I$(srcdir)/../../../html/document/src \
|
||||
-I$(srcdir)/../../../../layout/style \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
-I$(topsrcdir)/content/svg/content/src \
|
||||
$(NULL)
|
@ -20,3 +20,12 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/content/html/document/src',
|
||||
'/content/svg/content/src',
|
||||
'/content/xml/document/src',
|
||||
'/layout/style',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -1,18 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../../base/src \
|
||||
-I$(srcdir)/../../html/document/src \
|
||||
-I$(srcdir)/../../xml/document/src \
|
||||
-I$(srcdir)/../../xul/content/src \
|
||||
-I$(srcdir)/../../xul/document/src \
|
||||
-I$(srcdir)/../../events/src \
|
||||
-I$(srcdir)/../../../layout/style \
|
||||
-I$(srcdir)/../../../dom/base \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
$(NULL)
|
@ -44,3 +44,14 @@ MSVC_ENABLE_PGO = True
|
||||
|
||||
LIBRARY_NAME = 'gkconxbl_s'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/content/html/document/src',
|
||||
'/content/xml/document/src',
|
||||
'/content/xul/content/src',
|
||||
'/content/xul/document/src',
|
||||
'/dom/base',
|
||||
'/layout/style',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -1,18 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../../../html/document/src \
|
||||
-I$(srcdir)/../../../../layout/style \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../../xul/content/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(srcdir)/../../../../dom/base \
|
||||
-I$(srcdir)/../../../../caps/include \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
$(NULL)
|
@ -25,3 +25,13 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/caps/include',
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/content/html/document/src',
|
||||
'/content/xul/content/src',
|
||||
'/dom/base',
|
||||
'/layout/style',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -1,13 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../../public \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../xml \
|
||||
-I$(srcdir)/../xpath \
|
||||
-I$(srcdir)/../xslt \
|
||||
$(NULL)
|
@ -20,3 +20,9 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../public',
|
||||
'../xml',
|
||||
'../xpath',
|
||||
'../xslt',
|
||||
]
|
||||
|
@ -1,13 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../xpath \
|
||||
-I$(srcdir)/../xslt \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
$(NULL)
|
@ -17,3 +17,9 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../xpath',
|
||||
'../xslt',
|
||||
'/content/base/src',
|
||||
]
|
||||
|
@ -1,12 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir)/../xml \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../xslt \
|
||||
$(NULL)
|
@ -57,3 +57,8 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../xml',
|
||||
'../xslt',
|
||||
]
|
||||
|
@ -1,18 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# For nsDependentJSString
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/dom/base \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
INCLUDES += \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir)/../xml \
|
||||
-I$(srcdir)/../xpath \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
$(NULL)
|
@ -50,3 +50,12 @@ FAIL_ON_WARNINGS = True
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
# For nsDependentJSString
|
||||
LOCAL_INCLUDES += ["/dom/base"]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../xml',
|
||||
'../xpath',
|
||||
'/content/base/src',
|
||||
]
|
||||
|
@ -1,21 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir)/../../document/src \
|
||||
-I$(srcdir)/../../templates/src \
|
||||
-I$(srcdir)/../../../xml/content/src \
|
||||
-I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../../xml/document/src \
|
||||
-I$(srcdir)/../../../../layout/generic \
|
||||
-I$(srcdir)/../../../../layout/style \
|
||||
-I$(srcdir)/../../../../layout/xul/base/src \
|
||||
-I$(srcdir)/../../../html/content/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(srcdir)/../../../xbl/src \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
$(NULL)
|
@ -19,3 +19,17 @@ if CONFIG['MOZ_XUL']:
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../document/src',
|
||||
'../../templates/src',
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/content/html/content/src',
|
||||
'/content/xbl/src',
|
||||
'/content/xml/content/src',
|
||||
'/content/xml/document/src',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
'/layout/xul/base/src',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -1,20 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../content/src \
|
||||
-I$(srcdir)/../../templates/src \
|
||||
-I$(srcdir)/../../../../layout/base \
|
||||
-I$(srcdir)/../../../../layout/generic \
|
||||
-I$(srcdir)/../../../../layout/style \
|
||||
-I$(srcdir)/../../../../layout/xul/base/src \
|
||||
-I$(srcdir)/../../../xml/document/src \
|
||||
-I$(srcdir)/../../../xbl/src \
|
||||
-I$(srcdir)/../../../events/src \
|
||||
-I$(topsrcdir)/xpcom/ds \
|
||||
-I$(topsrcdir)/dom/base \
|
||||
$(NULL)
|
@ -27,3 +27,17 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/content/base/src',
|
||||
'/content/events/src',
|
||||
'/content/xbl/src',
|
||||
'/content/xml/document/src',
|
||||
'/content/xul/content/src',
|
||||
'/content/xul/templates/src',
|
||||
'/dom/base',
|
||||
'/layout/base',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
'/layout/xul/base/src',
|
||||
'/xpcom/ds',
|
||||
]
|
||||
|
@ -1,12 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = -I$(srcdir)/../../../base/src \
|
||||
-I$(srcdir)/../../content/src \
|
||||
-I$(srcdir)/../../../../dom/base \
|
||||
-I$(srcdir)/../../../../layout/xul/tree/ \
|
||||
$(NULL)
|
@ -41,3 +41,9 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../../content/src',
|
||||
'/content/base/src',
|
||||
'/dom/base',
|
||||
'/layout/xul/tree/',
|
||||
]
|
||||
|
@ -750,7 +750,6 @@ nsDocShell::nsDocShell():
|
||||
mIsAppTab(false),
|
||||
mUseGlobalHistory(false),
|
||||
mInPrivateBrowsing(false),
|
||||
mDeviceSizeIsPageSize(false),
|
||||
mFiredUnloadEvent(false),
|
||||
mEODForCurrentDocument(false),
|
||||
mURIResultedInDocument(false),
|
||||
@ -3933,27 +3932,6 @@ nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, bool* aOSHE)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::SetDeviceSizeIsPageSize(bool aValue)
|
||||
{
|
||||
if (mDeviceSizeIsPageSize != aValue) {
|
||||
mDeviceSizeIsPageSize = aValue;
|
||||
nsRefPtr<nsPresContext> presContext;
|
||||
GetPresContext(getter_AddRefs(presContext));
|
||||
if (presContext) {
|
||||
presContext->MediaFeatureValuesChanged(presContext->eAlwaysRebuildStyle);
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetDeviceSizeIsPageSize(bool* aValue)
|
||||
{
|
||||
*aValue = mDeviceSizeIsPageSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
|
||||
{
|
||||
|
@ -809,7 +809,6 @@ protected:
|
||||
bool mIsAppTab;
|
||||
bool mUseGlobalHistory;
|
||||
bool mInPrivateBrowsing;
|
||||
bool mDeviceSizeIsPageSize;
|
||||
|
||||
// This boolean is set to true right before we fire pagehide and generally
|
||||
// unset when we embed a new content viewer. While it's true no navigation
|
||||
|
@ -916,12 +916,4 @@ interface nsIDocShell : nsIDocShellTreeItem
|
||||
*/
|
||||
boolean isCommandEnabled(in string command);
|
||||
void doCommand(in string command);
|
||||
|
||||
/**
|
||||
* If deviceSizeIsPageSize is set to true, device-width/height media queries
|
||||
* will be calculated from the page size, not the device size.
|
||||
*
|
||||
* Used by the Responsive Design View.
|
||||
*/
|
||||
[infallible] attribute boolean deviceSizeIsPageSize;
|
||||
};
|
||||
|
@ -1,28 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORT_LIBRARY = 1
|
||||
SHARED_LIBRARY_LIBS= \
|
||||
../base/$(LIB_PREFIX)basedocshell_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/uriloader/base/$(LIB_PREFIX)uriloaderbase_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/uriloader/exthandler/$(LIB_PREFIX)exthandler_s.$(LIB_SUFFIX) \
|
||||
$(DEPTH)/uriloader/prefetch/$(LIB_PREFIX)prefetch_s.$(LIB_SUFFIX) \
|
||||
../shistory/src/$(LIB_PREFIX)shistory_s.$(LIB_SUFFIX) \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES = \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/../base \
|
||||
-I$(srcdir)/../shistory/src \
|
||||
-I$(topsrcdir)/uriloader/base \
|
||||
-I$(topsrcdir)/uriloader/prefetch \
|
||||
-I$(topsrcdir)/uriloader/exthandler \
|
||||
$(NULL)
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
LOCAL_INCLUDES += -I$(topsrcdir)/uriloader/exthandler/mac
|
||||
endif
|
@ -18,3 +18,13 @@ LIBRARY_NAME = 'docshell'
|
||||
|
||||
LIBXUL_LIBRARY = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../shistory/src/',
|
||||
'/uriloader/base',
|
||||
'/uriloader/exthandler',
|
||||
'/uriloader/prefetch',
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
||||
LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
|
||||
|
@ -1,8 +0,0 @@
|
||||
#
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
LOCAL_INCLUDES += -I$(srcdir)/../../base
|
@ -25,3 +25,6 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
MSVC_ENABLE_PGO = True
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/docshell/base',
|
||||
]
|
||||
|
@ -93,6 +93,10 @@ function isInaccessible(wnd, message) {
|
||||
}
|
||||
}
|
||||
|
||||
function getSubframe(win, i) {
|
||||
return SpecialPowers.unwrap(SpecialPowers.wrap(win)[i]);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Functions that require UniversalXPConnect privilege
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
@ -9,17 +9,18 @@
|
||||
iframe { width: 90%; height: 50px; }
|
||||
</style>
|
||||
<script>
|
||||
|
||||
window.onload = function () {
|
||||
navigateByLocation(window0.frames[0]);
|
||||
navigateByLocation(getSubframe(window0, 0));
|
||||
navigateByOpen("window1_child0");
|
||||
navigateByForm("window2_child0");
|
||||
navigateByHyperlink("window3_child0");
|
||||
|
||||
xpcWaitForFinishedFrames(function() {
|
||||
isInaccessible(window0.frames[0], "Should not be able to navigate off-domain frame by setting location.");
|
||||
isInaccessible(window1.frames[0], "Should not be able to navigate off-domain frame by calling window.open.");
|
||||
isInaccessible(window2.frames[0], "Should not be able to navigate off-domain frame by submitting form.");
|
||||
isInaccessible(window3.frames[0], "Should not be able to navigate off-domain frame by targeted hyperlink.");
|
||||
isInaccessible(getSubframe(window0, 0), "Should not be able to navigate off-domain frame by setting location.");
|
||||
isInaccessible(getSubframe(window1, 0), "Should not be able to navigate off-domain frame by calling window.open.");
|
||||
isInaccessible(getSubframe(window2, 0), "Should not be able to navigate off-domain frame by submitting form.");
|
||||
isInaccessible(getSubframe(window3, 0), "Should not be able to navigate off-domain frame by targeted hyperlink.");
|
||||
|
||||
window0.close();
|
||||
window1.close();
|
||||
|
@ -14,16 +14,16 @@ if (!navigator.platform.startsWith("Win")) {
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
navigateByLocation(frames[0].frames[0]);
|
||||
navigateByLocation(getSubframe(getSubframe(frames, 0), 0));
|
||||
navigateByOpen("child1_child0");
|
||||
navigateByForm("child2_child0");
|
||||
navigateByHyperlink("child3_child0");
|
||||
|
||||
xpcWaitForFinishedFrames(function() {
|
||||
isNavigated(frames[0].frames[0], "Should be able to navigate off-domain grandchild by setting location.");
|
||||
isNavigated(frames[1].frames[0], "Should be able to navigate off-domain grandchild by calling window.open.");
|
||||
isNavigated(frames[2].frames[0], "Should be able to navigate off-domain grandchild by submitting form.");
|
||||
isNavigated(frames[3].frames[0], "Should be able to navigate off-domain grandchild by targeted hyperlink.");
|
||||
isNavigated(getSubframe(getSubframe(frames, 0), 0), "Should be able to navigate off-domain grandchild by setting location.");
|
||||
isNavigated(getSubframe(getSubframe(frames, 1), 0), "Should be able to navigate off-domain grandchild by calling window.open.");
|
||||
isNavigated(getSubframe(getSubframe(frames, 2), 0), "Should be able to navigate off-domain grandchild by submitting form.");
|
||||
isNavigated(getSubframe(getSubframe(frames, 3), 0), "Should be able to navigate off-domain grandchild by targeted hyperlink.");
|
||||
|
||||
xpcCleanupWindows();
|
||||
SimpleTest.finish();
|
||||
|
@ -11,7 +11,7 @@
|
||||
<script>
|
||||
window.onload = function () {
|
||||
document.getElementById('active').innerHTML =
|
||||
'<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#parent.frames[0],location"></iframe>' +
|
||||
'<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#SpecialPowers.unwrap(SpecialPowers.wrap(parent).frames[0]),location"></iframe>' +
|
||||
'<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child1,open"></iframe>' +
|
||||
'<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child2,form"></iframe>' +
|
||||
'<iframe src="http://test1.example.org:80/tests/docshell/test/navigation/navigate.html#child3,hyperlink"></iframe>';
|
||||
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -34,3 +34,6 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
LIBRARY_NAME = 'dom_activities_s'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
@ -385,19 +385,6 @@ nsScreen::SlowMozUnlockOrientation()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool
|
||||
nsScreen::IsDeviceSizePageSize()
|
||||
{
|
||||
nsPIDOMWindow* owner = GetOwner();
|
||||
if (owner) {
|
||||
nsIDocShell* docShell = owner->GetDocShell();
|
||||
if (docShell) {
|
||||
return docShell->GetDeviceSizeIsPageSize();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
JSObject*
|
||||
nsScreen::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
|
@ -51,15 +51,6 @@ public:
|
||||
int32_t GetWidth(ErrorResult& aRv)
|
||||
{
|
||||
nsRect rect;
|
||||
if (IsDeviceSizePageSize()) {
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
if (owner) {
|
||||
int32_t innerWidth = 0;
|
||||
aRv = owner->GetInnerWidth(&innerWidth);
|
||||
return innerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
aRv = GetRect(rect);
|
||||
return rect.width;
|
||||
}
|
||||
@ -67,15 +58,6 @@ public:
|
||||
int32_t GetHeight(ErrorResult& aRv)
|
||||
{
|
||||
nsRect rect;
|
||||
if (IsDeviceSizePageSize()) {
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
if (owner) {
|
||||
int32_t innerHeight = 0;
|
||||
aRv = owner->GetInnerHeight(&innerHeight);
|
||||
return innerHeight;
|
||||
}
|
||||
}
|
||||
|
||||
aRv = GetRect(rect);
|
||||
return rect.height;
|
||||
}
|
||||
@ -155,8 +137,6 @@ private:
|
||||
|
||||
LockPermission GetLockOrientationPermission() const;
|
||||
|
||||
bool IsDeviceSizePageSize();
|
||||
|
||||
nsRefPtr<FullScreenEventListener> mEventListener;
|
||||
};
|
||||
|
||||
|
@ -3505,7 +3505,12 @@ for (uint32_t i = 0; i < length; ++i) {
|
||||
raise NoSuchDescriptorError("Can't handle member callbacks in "
|
||||
"workers; need to sort out rooting"
|
||||
"issues")
|
||||
declType = CGGeneric("JS::Rooted<JSObject*>")
|
||||
if isOptional:
|
||||
# We have a specialization of Optional that will use a
|
||||
# Rooted for the storage here.
|
||||
declType = CGGeneric("JS::Handle<JSObject*>")
|
||||
else:
|
||||
declType = CGGeneric("JS::Rooted<JSObject*>")
|
||||
conversion = " ${declName} = &${val}.toObject();\n"
|
||||
declArgs = "cx"
|
||||
else:
|
||||
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -61,3 +61,6 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
LIBRARY_NAME = 'domfile_s'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
]
|
||||
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,5 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/dom/dom-config.mk
|
@ -1,8 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
LOCAL_INCLUDES += \
|
||||
-I$(topsrcdir)/dom/workers \
|
||||
-I$(topsrcdir)/dom/base \
|
||||
$(NULL)
|
@ -25,3 +25,7 @@ LIBXUL_LIBRARY = True
|
||||
|
||||
LIBRARY_NAME = 'dompromise_s'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../workers',
|
||||
]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user