зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team.
This commit is contained in:
Коммит
734b934f05
|
@ -34,6 +34,13 @@ midl_enums = [
|
|||
'IA2CommonTypes',
|
||||
]
|
||||
|
||||
EXPORTS += [x + '.h' for x in midl_enums]
|
||||
EXPORTS += [x + '.h' for x in midl_interfaces]
|
||||
EXPORTS += [x + '_i.c' for x in midl_interfaces]
|
||||
headers = ['%s.h' % x for x in midl_enums]
|
||||
interfaces_h = ['%s.h' % x for x in midl_interfaces]
|
||||
interfaces_c = ['%s_i.c' % x for x in midl_interfaces]
|
||||
|
||||
# The underscore throws off sorting and EXPORTS expects sorted lists.
|
||||
interfaces_c.sort()
|
||||
|
||||
EXPORTS += headers
|
||||
EXPORTS += interfaces_h
|
||||
EXPORTS += interfaces_c
|
||||
|
|
|
@ -951,12 +951,6 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
|||
nsresult rv = Accessible::HandleAccEvent(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return FirePlatformEvent(aEvent);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
||||
{
|
||||
Accessible* accessible = aEvent->GetAccessible();
|
||||
NS_ENSURE_TRUE(accessible, NS_ERROR_FAILURE);
|
||||
|
||||
|
|
|
@ -75,7 +75,6 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual nsresult FirePlatformEvent(AccEvent* aEvent);
|
||||
|
||||
nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject *aObject);
|
||||
nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject);
|
||||
|
|
|
@ -881,16 +881,6 @@ protected:
|
|||
*/
|
||||
AccGroupInfo* GetGroupInfo();
|
||||
|
||||
/**
|
||||
* Fires platform accessible event. It's notification method only. It does
|
||||
* change nothing on Gecko side. Don't use it until you're sure what you do
|
||||
* (see example in XUL tree accessible), use nsEventShell::FireEvent()
|
||||
* instead. MUST be overridden in wrap classes.
|
||||
*
|
||||
* @param aEvent the accessible event to fire.
|
||||
*/
|
||||
virtual nsresult FirePlatformEvent(AccEvent* aEvent) = 0;
|
||||
|
||||
// Data Members
|
||||
nsRefPtr<Accessible> mParent;
|
||||
nsTArray<nsRefPtr<Accessible> > mChildren;
|
||||
|
|
|
@ -1014,32 +1014,24 @@ HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
|
|||
return NS_OK;
|
||||
|
||||
case BOUNDARY_WORD_START: {
|
||||
if (offset == 0) { // no word before 0 offset
|
||||
*aStartOffset = *aEndOffset = 0;
|
||||
return NS_OK;
|
||||
// If the offset is a word start (except text length offset) then move
|
||||
// backward to find a start offset (end offset is the given offset).
|
||||
// Otherwise move backward twice to find both start and end offsets.
|
||||
if (offset == CharacterCount()) {
|
||||
*aEndOffset = FindWordBoundary(offset, eDirPrevious, eStartWord);
|
||||
*aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
|
||||
} else {
|
||||
*aStartOffset = FindWordBoundary(offset, eDirPrevious, eStartWord);
|
||||
*aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eStartWord);
|
||||
if (*aEndOffset != offset) {
|
||||
*aEndOffset = *aStartOffset;
|
||||
*aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
|
||||
}
|
||||
}
|
||||
|
||||
// If the offset is a word start then move backward to find start offset
|
||||
// (end offset is the given offset). Otherwise move backward twice to find
|
||||
// both start and end offsets.
|
||||
int32_t midOffset = FindWordBoundary(offset, eDirPrevious, eStartWord);
|
||||
*aEndOffset = FindWordBoundary(midOffset, eDirNext, eStartWord);
|
||||
if (*aEndOffset == offset) {
|
||||
*aStartOffset = midOffset;
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
}
|
||||
|
||||
*aStartOffset = FindWordBoundary(midOffset, eDirPrevious, eStartWord);
|
||||
*aEndOffset = midOffset;
|
||||
return GetText(*aStartOffset, *aEndOffset, aText);
|
||||
}
|
||||
|
||||
case BOUNDARY_WORD_END: {
|
||||
if (offset == 0) { // no word before 0 offset
|
||||
*aStartOffset = *aEndOffset = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Move word backward twice to find start and end offsets.
|
||||
*aEndOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
|
||||
*aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
|
||||
|
|
|
@ -73,8 +73,6 @@ public: // construction, destruction
|
|||
|
||||
protected:
|
||||
|
||||
virtual nsresult FirePlatformEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Return true if the parent doesn't have children to expose to AT.
|
||||
*/
|
||||
|
|
|
@ -139,16 +139,6 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
|||
nsresult rv = Accessible::HandleAccEvent(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return FirePlatformEvent(aEvent);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
uint32_t eventType = aEvent->GetEventType();
|
||||
|
||||
// ignore everything but focus-changed, value-changed, caret and selection
|
||||
|
|
|
@ -21,12 +21,6 @@ class AccessibleWrap : public Accessible
|
|||
public: // construction, destruction
|
||||
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
|
||||
virtual ~AccessibleWrap();
|
||||
|
||||
protected:
|
||||
virtual nsresult FirePlatformEvent(AccEvent* aEvent)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
|
|
@ -1505,15 +1505,6 @@ AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
|||
nsresult rv = Accessible::HandleAccEvent(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return FirePlatformEvent(aEvent);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccessibleWrap
|
||||
|
||||
nsresult
|
||||
AccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
||||
{
|
||||
// Don't fire native MSAA events or mess with the system caret
|
||||
// when running in metro mode. This confuses input focus tracking
|
||||
// in metro's UIA implementation.
|
||||
|
@ -1583,6 +1574,9 @@ AccessibleWrap::FirePlatformEvent(AccEvent* aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccessibleWrap
|
||||
|
||||
//------- Helper methods ---------
|
||||
|
||||
int32_t
|
||||
|
|
|
@ -243,7 +243,6 @@ public: // construction, destruction
|
|||
static IDispatch *NativeAccessible(nsIAccessible *aXPAccessible);
|
||||
|
||||
protected:
|
||||
virtual nsresult FirePlatformEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Creates ITypeInfo for LIBID_Accessibility if it's needed and returns it.
|
||||
|
|
|
@ -105,7 +105,17 @@ function testCharAtOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
}
|
||||
|
||||
/**
|
||||
* Test getTextAtOffset function over different elements
|
||||
* Test getTextAtOffset function over different elements.
|
||||
*
|
||||
* @param aIDs [in] ID or array of IDs
|
||||
* @param aBoundaryType [in] boundary type for text to be retrieved
|
||||
* @param aTestList [in] array of sets:
|
||||
* offset1 and offset2 defining the offset range
|
||||
* the text in the range
|
||||
* start offset of the text in the range
|
||||
* end offset of the text in the range
|
||||
*
|
||||
* or
|
||||
*
|
||||
* @param aOffset [in] the offset to get the text at
|
||||
* @param aBoundaryType [in] Boundary type for text to be retrieved
|
||||
|
@ -118,35 +128,9 @@ function testCharAtOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
* kTodo or kOk for returned start offset
|
||||
* kTodo or kOk for returned offset result
|
||||
*/
|
||||
function testTextAtOffset(aOffset, aBoundaryType, aText,
|
||||
aStartOffset, aEndOffset)
|
||||
function testTextAtOffset()
|
||||
{
|
||||
// List of IDs.
|
||||
if (arguments[5] instanceof Array) {
|
||||
var ids = arguments[5];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var acc = getAccessible(ids[i], nsIAccessibleText);
|
||||
testTextHelper(ids[i], aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
kOk, kOk, kOk,
|
||||
acc.getTextAtOffset, "getTextAtOffset ");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 5; i < arguments.length; i = i + 4) {
|
||||
var ID = arguments[i];
|
||||
var acc = getAccessible(ID, nsIAccessibleText);
|
||||
var toDoFlag1 = arguments[i + 1];
|
||||
var toDoFlag2 = arguments[i + 2];
|
||||
var toDoFlag3 = arguments[i + 3];
|
||||
|
||||
testTextHelper(ID, aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
toDoFlag1, toDoFlag2, toDoFlag3,
|
||||
acc.getTextAtOffset, "getTextAtOffset ");
|
||||
}
|
||||
testTextSuperHelper("getTextAtOffset", arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,6 +158,16 @@ function testCharAfterOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
/**
|
||||
* Test getTextAfterOffset function over different elements
|
||||
*
|
||||
* @param aIDs [in] ID or array of IDs
|
||||
* @param aBoundaryType [in] boundary type for text to be retrieved
|
||||
* @param aTestList [in] array of sets:
|
||||
* offset1 and offset2 defining the offset range
|
||||
* the text in the range
|
||||
* start offset of the text in the range
|
||||
* end offset of the text in the range
|
||||
*
|
||||
* or
|
||||
*
|
||||
* @param aOffset [in] the offset to get the text after
|
||||
* @param aBoundaryType [in] Boundary type for text to be retrieved
|
||||
* @param aText [in] expected return text for getTextAfterOffset
|
||||
|
@ -188,33 +182,7 @@ function testCharAfterOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
function testTextAfterOffset(aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset)
|
||||
{
|
||||
// List of IDs.
|
||||
if (arguments[5] instanceof Array) {
|
||||
var ids = arguments[5];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var acc = getAccessible(ids[i], nsIAccessibleText);
|
||||
testTextHelper(ids[i], aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
kOk, kOk, kOk,
|
||||
acc.getTextAfterOffset, "getTextAfterOffset ");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// List of tuples.
|
||||
for (var i = 5; i < arguments.length; i = i + 4) {
|
||||
var ID = arguments[i];
|
||||
var acc = getAccessible(ID, nsIAccessibleText);
|
||||
var toDoFlag1 = arguments[i + 1];
|
||||
var toDoFlag2 = arguments[i + 2];
|
||||
var toDoFlag3 = arguments[i + 3];
|
||||
|
||||
testTextHelper(ID, aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
toDoFlag1, toDoFlag2, toDoFlag3,
|
||||
acc.getTextAfterOffset, "getTextAfterOffset ");
|
||||
}
|
||||
testTextSuperHelper("getTextAfterOffset", arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,6 +210,16 @@ function testCharBeforeOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
/**
|
||||
* Test getTextBeforeOffset function over different elements
|
||||
*
|
||||
* @param aIDs [in] ID or array of IDs
|
||||
* @param aBoundaryType [in] boundary type for text to be retrieved
|
||||
* @param aTestList [in] array of sets:
|
||||
* offset1 and offset2 defining the offset range
|
||||
* the text in the range
|
||||
* start offset of the text in the range
|
||||
* end offset of the text in the range
|
||||
*
|
||||
* or
|
||||
*
|
||||
* @param aOffset [in] the offset to get the text before
|
||||
* @param aBoundaryType [in] Boundary type for text to be retrieved
|
||||
* @param aText [in] expected return text for getTextBeforeOffset
|
||||
|
@ -256,32 +234,7 @@ function testCharBeforeOffset(aIDs, aOffset, aChar, aStartOffset, aEndOffset)
|
|||
function testTextBeforeOffset(aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset)
|
||||
{
|
||||
// List of IDs.
|
||||
if (arguments[5] instanceof Array) {
|
||||
var ids = arguments[5];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var acc = getAccessible(ids[i], nsIAccessibleText);
|
||||
testTextHelper(ids[i], aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
kOk, kOk, kOk,
|
||||
acc.getTextBeforeOffset, "getTextBeforeOffset ");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 5; i < arguments.length; i = i + 4) {
|
||||
var ID = arguments[i];
|
||||
var acc = getAccessible(ID, nsIAccessibleText);
|
||||
var toDoFlag1 = arguments[i + 1];
|
||||
var toDoFlag2 = arguments[i + 2];
|
||||
var toDoFlag3 = arguments[i + 3];
|
||||
|
||||
testTextHelper(ID, aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
toDoFlag1, toDoFlag2, toDoFlag3,
|
||||
acc.getTextBeforeOffset, "getTextBeforeOffset ");
|
||||
}
|
||||
testTextSuperHelper("getTextBeforeOffset", arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -502,6 +455,78 @@ function testTextGetSelection(aID, aStartOffset, aEndOffset, aSelectionIndex)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
|
||||
function testTextSuperHelper(aFuncName, aArgs)
|
||||
{
|
||||
// List of tests.
|
||||
if (aArgs[2] instanceof Array) {
|
||||
var ids = (aArgs[0] instanceof Array) ? aArgs[0] : [ aArgs[0] ];
|
||||
var boundaryType = aArgs[1];
|
||||
var list = aArgs[2];
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var offset1 = list[i][0], offset2 = list[i][1];
|
||||
var text = list[i][2], startOffset = list[i][3], endOffset = list[i][4];
|
||||
var failureList = list[i][5];
|
||||
for (var offset = offset1; offset <= offset2; offset++) {
|
||||
for (var idIdx = 0; idIdx < ids.length; idIdx++) {
|
||||
var id = ids[idIdx];
|
||||
|
||||
var flagOk1 = kOk, flagOk2 = kOk, flagOk3 = kOk;
|
||||
if (failureList) {
|
||||
for (var fIdx = 0; fIdx < failureList.length; fIdx++) {
|
||||
if (offset == failureList[fIdx][0] && id == failureList[fIdx][1]) {
|
||||
flagOk1 = failureList[fIdx][2];
|
||||
flagOk2 = failureList[fIdx][3];
|
||||
flagOk3 = failureList[fIdx][4];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var acc = getAccessible(id, nsIAccessibleText);
|
||||
testTextHelper(id, offset, boundaryType,
|
||||
text, startOffset, endOffset,
|
||||
flagOk1, flagOk2, flagOk3,
|
||||
acc[aFuncName], aFuncName + " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Test at single offset. List of IDs.
|
||||
var offset = aArgs[0];
|
||||
var boundaryType = aArgs[1];
|
||||
var text = aArgs[2];
|
||||
var startOffset = aArgs[3];
|
||||
var endOffset = aArgs[4];
|
||||
if (aArgs[5] instanceof Array) {
|
||||
var ids = aArgs[5];
|
||||
for (var i = 0; i < ids.length; i++) {
|
||||
var acc = getAccessible(ids[i], nsIAccessibleText);
|
||||
testTextHelper(ids[i], offset, boundaryType,
|
||||
text, startOffset, endOffset,
|
||||
kOk, kOk, kOk,
|
||||
acc[aFuncName], aFuncName + " ");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Each ID is tested separately.
|
||||
for (var i = 5; i < aArgs.length; i = i + 4) {
|
||||
var ID = aArgs[i];
|
||||
var acc = getAccessible(ID, nsIAccessibleText);
|
||||
var toDoFlag1 = aArgs[i + 1];
|
||||
var toDoFlag2 = aArgs[i + 2];
|
||||
var toDoFlag3 = aArgs[i + 3];
|
||||
|
||||
testTextHelper(ID, offset, boundaryType,
|
||||
text, startOffset, endOffset,
|
||||
toDoFlag1, toDoFlag2, toDoFlag3,
|
||||
acc[aFuncName], aFuncName + " ");
|
||||
}
|
||||
}
|
||||
|
||||
function testTextHelper(aID, aOffset, aBoundaryType,
|
||||
aText, aStartOffset, aEndOffset,
|
||||
aToDoFlag1, aToDoFlag2, aToDoFlag3,
|
||||
|
|
|
@ -22,6 +22,7 @@ MOCHITEST_A11Y_FILES = \
|
|||
test_selection.html \
|
||||
test_singleline.html \
|
||||
test_whitespaces.html \
|
||||
test_wordboundary.html \
|
||||
test_words.html \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@
|
|||
testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9, IDs);
|
||||
testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
|
||||
testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
|
||||
testTextBeforeOffset(19, BOUNDARY_WORD_START, "words\n", 13, 19, IDs);
|
||||
testTextBeforeOffset(19, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
|
||||
|
||||
// BOUNDARY_WORD_END
|
||||
testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
|
||||
|
|
|
@ -147,7 +147,11 @@
|
|||
testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
|
||||
testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
|
||||
testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
|
||||
testTextBeforeOffset(15, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
|
||||
testTextBeforeOffset(15, BOUNDARY_WORD_START, "my ", 6, 9,
|
||||
"input", kOk, kOk, kOk,
|
||||
"div", kOk, kOk, kOk,
|
||||
"editable", kOk, kOk, kOk,
|
||||
"textarea", kTodo, kTodo, kTodo);
|
||||
|
||||
// BOUNDARY_WORD_END
|
||||
testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Word boundary text tests</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../text.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest()
|
||||
{
|
||||
// "hello"
|
||||
var ids = [ "i1", "d1", "e1" ];
|
||||
testTextBeforeOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 5, "", 0, 0 ] ]);
|
||||
testTextBeforeOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 5, "", 0, 0 ] ]);
|
||||
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 5, "hello", 0, 5 ] ]);
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 5, "hello", 0, 5 ] ]);
|
||||
|
||||
testTextAfterOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 5, "", 5, 5 ] ]);
|
||||
testTextAfterOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 5, "", 5, 5 ] ]);
|
||||
|
||||
// "hello "
|
||||
var ids = [ "i2", "d2", "e2" ];
|
||||
testTextBeforeOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 6, "", 0, 0 ] ]);
|
||||
testTextBeforeOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 5, "", 0, 0 ],
|
||||
[ 6, 6, "hello", 0, 5,
|
||||
[ [6, "e2", kTodo, kOk, kTodo ] ]
|
||||
]
|
||||
]);
|
||||
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 6, "hello ", 0, 6 ] ]);
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 4, "hello", 0, 5 ],
|
||||
[ 5, 6, " ", 5, 6,
|
||||
[ [ 5, "e2", kTodo, kTodo, kOk ],
|
||||
[ 6, "e2", kTodo, kTodo, kOk ] ]
|
||||
]
|
||||
]);
|
||||
|
||||
testTextAfterOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 6, "", 6, 6 ] ]);
|
||||
testTextAfterOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 5, " ", 5, 6,
|
||||
[ [ 5, "e2", kTodo, kTodo, kOk ] ]
|
||||
],
|
||||
[ 6, 6, "", 6, 6 ]
|
||||
]);
|
||||
|
||||
// "hello all"
|
||||
ids = [ "i6", "d6", "e6" ];
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 5, "hello ", 0, 6 ],
|
||||
[ 6, 9, "all", 6, 9 ] ]);
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 4, "hello", 0, 5 ],
|
||||
[ 5, 9, " all", 5, 9 ] ]);
|
||||
|
||||
// "hello my friend"
|
||||
ids = [ "i7", "d7", "e7" ];
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_START,
|
||||
[ [ 0, 5, "hello ", 0, 6 ],
|
||||
[ 6, 8, "my ", 6, 9 ],
|
||||
[ 9, 15, "friend", 9, 15] ]);
|
||||
testTextAtOffset(ids, BOUNDARY_WORD_END,
|
||||
[ [ 0, 4, "hello", 0, 5 ],
|
||||
[ 5, 7, " my", 5, 8 ],
|
||||
[ 8, 15, " friend", 8, 15] ]);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<input id="i1" value="hello"/>
|
||||
<div id="d1">hello</div>
|
||||
<div id="e1" contenteditable="true">hello</div>
|
||||
|
||||
<input id="i2" value="hello "/>
|
||||
<pre><div id="d2">hello </div></pre>
|
||||
<div id="e2" contenteditable="true">hello </div>
|
||||
|
||||
<input id="i6" value="hello all"/>
|
||||
<div id="d6">hello all</div>
|
||||
<div id="e6" contenteditable="true">hello all</div>
|
||||
|
||||
<input id="i7" value="hello my friend"/>
|
||||
<div id="d7">hello my friend</div>
|
||||
<div id="e7" contenteditable="true">hello my friend</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1230,6 +1230,9 @@ pref("social.toast-notifications.enabled", true);
|
|||
|
||||
pref("dom.identity.enabled", false);
|
||||
|
||||
// Turn on the CSP 1.0 parser for Content Security Policy headers
|
||||
pref("security.csp.speccompliant", true);
|
||||
|
||||
// Block insecure active content on https pages
|
||||
pref("security.mixed_content.block_active_content", true);
|
||||
|
||||
|
|
|
@ -162,8 +162,10 @@ function test_TestEventListeners()
|
|||
test_clicks("MozTapGesture", 3);
|
||||
test_clicks("MozPressTapGesture", 1);
|
||||
|
||||
// simple delivery test for edgeui gesture
|
||||
e("MozEdgeUIGesture", 0, 0, 0);
|
||||
// simple delivery test for edgeui gestures
|
||||
e("MozEdgeUIStarted", 0, 0, 0);
|
||||
e("MozEdgeUICanceled", 0, 0, 0);
|
||||
e("MozEdgeUICompleted", 0, 0, 0);
|
||||
|
||||
// event.shiftKey
|
||||
let modifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
|
||||
|
|
|
@ -147,7 +147,7 @@ var gPrivacyPane = {
|
|||
getTrackingPrefs: function PPP_getTrackingPrefs()
|
||||
{
|
||||
// XXX avoid using bindings that might not be attached, see bug 859982
|
||||
let dntValue = Services.prefs.getBoolPref("privacy.donottrackheader.value"),
|
||||
let dntValue = Services.prefs.getIntPref("privacy.donottrackheader.value"),
|
||||
dntEnabled = Services.prefs.getBoolPref("privacy.donottrackheader.enabled");
|
||||
|
||||
// if DNT is enbaled, select the value from the selected radio
|
||||
|
|
|
@ -86,8 +86,10 @@ function test() {
|
|||
|
||||
ok(requestItem.attachment.requestHeaders,
|
||||
"There should be a requestHeaders attachment available.");
|
||||
is(requestItem.attachment.requestHeaders.headers.length, 7,
|
||||
ok(requestItem.attachment.requestHeaders.headers.length >= 6,
|
||||
"The requestHeaders attachment has an incorrect |headers| property.");
|
||||
// Can't test for an exact total number of headers, because it seems to
|
||||
// vary across pgo/non-pgo builds.
|
||||
isnot(requestItem.attachment.requestHeaders.headersSize, 0,
|
||||
"The requestHeaders attachment has an incorrect |headersSize| property.");
|
||||
// Can't test for the exact request headers size because the value may
|
||||
|
|
|
@ -63,8 +63,11 @@ function test() {
|
|||
|
||||
is(tabpanel.querySelectorAll(".variables-view-scope").length, 2,
|
||||
"There should be 2 header scopes displayed in this tabpanel.");
|
||||
is(tabpanel.querySelectorAll(".variable-or-property").length, 13,
|
||||
"There should be 13 header values displayed in this tabpanel.");
|
||||
ok(tabpanel.querySelectorAll(".variable-or-property").length >= 12,
|
||||
"There should be at least 12 header values displayed in this tabpanel.");
|
||||
// Can't test for an exact total number of headers, because it seems to
|
||||
// vary across pgo/non-pgo builds.
|
||||
|
||||
is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
|
||||
"The empty notice should not be displayed in this tabpanel.");
|
||||
|
||||
|
@ -108,10 +111,18 @@ function test() {
|
|||
"Connection", "The penultimate request header name was incorrect.");
|
||||
is(requestScope.querySelectorAll(".variables-view-variable .value")[5].getAttribute("value"),
|
||||
"\"keep-alive\"", "The penultimate request header value was incorrect.");
|
||||
is(requestScope.querySelectorAll(".variables-view-variable .name")[6].getAttribute("value"),
|
||||
"Cache-Control", "The last request header name was incorrect.");
|
||||
is(requestScope.querySelectorAll(".variables-view-variable .value")[6].getAttribute("value"),
|
||||
"\"max-age=0\"", "The last request header value was incorrect.");
|
||||
|
||||
let lastReqHeaderName = requestScope.querySelectorAll(".variables-view-variable .name")[6];
|
||||
let lastReqHeaderValue = requestScope.querySelectorAll(".variables-view-variable .value")[6];
|
||||
if (lastReqHeaderName && lastReqHeaderValue) {
|
||||
is(lastReqHeaderName.getAttribute("value"),
|
||||
"Cache-Control", "The last request header name was incorrect.");
|
||||
is(lastReqHeaderValue.getAttribute("value"),
|
||||
"\"max-age=0\"", "The last request header value was incorrect.");
|
||||
} else {
|
||||
info("The number of request headers was 6 instead of 7. Technically, " +
|
||||
"not a failure in this particular test, but needs investigation.");
|
||||
}
|
||||
}
|
||||
|
||||
function testCookiesTab() {
|
||||
|
|
|
@ -46,7 +46,7 @@ const BrowserTouchHandler = {
|
|||
// Send the MozEdgeUIGesture to input.js to
|
||||
// toggle the context ui.
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUIGesture", true, false);
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -290,8 +290,7 @@ var ContentAreaObserver = {
|
|||
},
|
||||
|
||||
_getContentHeightForWindow: function (windowHeight) {
|
||||
let contextUIHeight = BrowserUI.isTabsOnly ? Elements.toolbar.getBoundingClientRect().bottom : 0;
|
||||
return windowHeight - contextUIHeight;
|
||||
return windowHeight;
|
||||
},
|
||||
|
||||
_getViewableHeightForContent: function (contentHeight) {
|
||||
|
|
|
@ -96,7 +96,6 @@ var BrowserUI = {
|
|||
window.addEventListener("MozPrecisePointer", this, true);
|
||||
window.addEventListener("MozImprecisePointer", this, true);
|
||||
|
||||
Services.prefs.addObserver("browser.tabs.tabsOnly", this, false);
|
||||
Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
|
||||
Services.obs.addObserver(this, "metro_viewstate_changed", false);
|
||||
|
||||
|
@ -143,7 +142,6 @@ var BrowserUI = {
|
|||
messageManager.addMessageListener("Browser:MozApplicationManifest", OfflineApps);
|
||||
|
||||
try {
|
||||
BrowserUI._updateTabsOnly();
|
||||
Downloads.init();
|
||||
DialogUI.init();
|
||||
FormHelperUI.init();
|
||||
|
@ -402,23 +400,18 @@ var BrowserUI = {
|
|||
},
|
||||
|
||||
animateClosingTab: function animateClosingTab(tabToClose) {
|
||||
if (this.isTabsOnly) {
|
||||
Browser.closeTab(tabToClose, { forceClose: true } );
|
||||
} else {
|
||||
// Trigger closing animation
|
||||
tabToClose.chromeTab.setAttribute("closing", "true");
|
||||
tabToClose.chromeTab.setAttribute("closing", "true");
|
||||
|
||||
let wasCollapsed = !ContextUI.isExpanded;
|
||||
if (wasCollapsed) {
|
||||
ContextUI.displayTabs();
|
||||
}
|
||||
let wasCollapsed = !ContextUI.isExpanded;
|
||||
if (wasCollapsed) {
|
||||
ContextUI.displayTabs();
|
||||
}
|
||||
|
||||
this.setOnTabAnimationEnd(function() {
|
||||
Browser.closeTab(tabToClose, { forceClose: true } );
|
||||
this.setOnTabAnimationEnd(function() {
|
||||
Browser.closeTab(tabToClose, { forceClose: true } );
|
||||
if (wasCollapsed)
|
||||
ContextUI.dismissWithDelay(kNewTabAnimationDelayMsec);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -525,32 +518,10 @@ var BrowserUI = {
|
|||
ContextUI.cancelDismiss();
|
||||
},
|
||||
|
||||
|
||||
/*********************************
|
||||
* Conventional tabs
|
||||
*/
|
||||
|
||||
// Tabsonly displays the url bar with conventional tabs. Also
|
||||
// the tray does not auto hide.
|
||||
get isTabsOnly() {
|
||||
return Services.prefs.getBoolPref("browser.tabs.tabsOnly");
|
||||
},
|
||||
|
||||
_updateTabsOnly: function _updateTabsOnly() {
|
||||
if (this.isTabsOnly) {
|
||||
Elements.windowState.setAttribute("tabsonly", "true");
|
||||
} else {
|
||||
Elements.windowState.removeAttribute("tabsonly");
|
||||
}
|
||||
},
|
||||
|
||||
observe: function BrowserUI_observe(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "nsPref:changed":
|
||||
switch (aData) {
|
||||
case "browser.tabs.tabsOnly":
|
||||
this._updateTabsOnly();
|
||||
break;
|
||||
case "browser.cache.disk_cache_ssl":
|
||||
this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
|
||||
break;
|
||||
|
@ -1119,7 +1090,9 @@ var ContextUI = {
|
|||
Elements.browsers.addEventListener("mousedown", this, true);
|
||||
Elements.browsers.addEventListener("touchstart", this, true);
|
||||
Elements.browsers.addEventListener("AlertActive", this, true);
|
||||
window.addEventListener("MozEdgeUIGesture", this, true);
|
||||
window.addEventListener("MozEdgeUIStarted", this, true);
|
||||
window.addEventListener("MozEdgeUICanceled", this, true);
|
||||
window.addEventListener("MozEdgeUICompleted", this, true);
|
||||
window.addEventListener("keypress", this, true);
|
||||
window.addEventListener("KeyboardChanged", this, false);
|
||||
|
||||
|
@ -1284,8 +1257,7 @@ var ContextUI = {
|
|||
|
||||
// tab tray state
|
||||
_setIsExpanded: function _setIsExpanded(aFlag, setSilently) {
|
||||
// if the tray can't be expanded because we're in
|
||||
// tabsonly mode, don't expand it.
|
||||
// if the tray can't be expanded, don't expand it.
|
||||
if (!this.isExpandable || this.isExpanded == aFlag)
|
||||
return;
|
||||
|
||||
|
@ -1313,7 +1285,29 @@ var ContextUI = {
|
|||
* Events
|
||||
*/
|
||||
|
||||
_onEdgeUIEvent: function _onEdgeUIEvent(aEvent) {
|
||||
_onEdgeUIStarted: function(aEvent) {
|
||||
this._hasEdgeSwipeStarted = true;
|
||||
this._clearDelayedTimeout();
|
||||
|
||||
if (StartUI.hide()) {
|
||||
this.dismiss();
|
||||
return;
|
||||
}
|
||||
this.toggle();
|
||||
},
|
||||
|
||||
_onEdgeUICanceled: function(aEvent) {
|
||||
this._hasEdgeSwipeStarted = false;
|
||||
StartUI.hide();
|
||||
this.dismiss();
|
||||
},
|
||||
|
||||
_onEdgeUICompleted: function(aEvent) {
|
||||
if (this._hasEdgeSwipeStarted) {
|
||||
this._hasEdgeSwipeStarted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this._clearDelayedTimeout();
|
||||
if (StartUI.hide()) {
|
||||
this.dismiss();
|
||||
|
@ -1324,8 +1318,14 @@ var ContextUI = {
|
|||
|
||||
handleEvent: function handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "MozEdgeUIGesture":
|
||||
this._onEdgeUIEvent(aEvent);
|
||||
case "MozEdgeUIStarted":
|
||||
this._onEdgeUIStarted(aEvent);
|
||||
break;
|
||||
case "MozEdgeUICanceled":
|
||||
this._onEdgeUICanceled(aEvent);
|
||||
break;
|
||||
case "MozEdgeUICompleted":
|
||||
this._onEdgeUICompleted(aEvent);
|
||||
break;
|
||||
case "mousedown":
|
||||
if (aEvent.button == 0 && this.isVisible)
|
||||
|
@ -1471,7 +1471,7 @@ var StartUI = {
|
|||
break;
|
||||
case "contextmenu":
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUIGesture", true, false);
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -98,12 +98,12 @@ var Browser = {
|
|||
ContentAreaObserver.init();
|
||||
|
||||
function fullscreenHandler() {
|
||||
if (!window.fullScreen)
|
||||
Elements.toolbar.setAttribute("fullscreen", "true");
|
||||
if (Browser.selectedBrowser.contentWindow.document.mozFullScreenElement)
|
||||
Elements.stack.setAttribute("fullscreen", "true");
|
||||
else
|
||||
Elements.toolbar.removeAttribute("fullscreen");
|
||||
Elements.stack.removeAttribute("fullscreen");
|
||||
}
|
||||
window.addEventListener("fullscreen", fullscreenHandler, false);
|
||||
window.addEventListener("mozfullscreenchange", fullscreenHandler, true);
|
||||
|
||||
BrowserUI.init();
|
||||
|
||||
|
|
|
@ -367,11 +367,16 @@
|
|||
</toolbar>
|
||||
</appbar>
|
||||
|
||||
<!-- Selection overlay - this should be below any content that can have selectable text -->
|
||||
<!-- onclick addresses dom bug 835175, str in bug 832957 -->
|
||||
<box onclick="false" class="selection-overlay-hidden" id="selection-overlay"/>
|
||||
<!-- Selection overlay - this should be below any content that can have selectable text -->
|
||||
<!-- onclick addresses dom bug 835175, str in bug 832957 -->
|
||||
<box onclick="false" class="selection-overlay-hidden" id="selection-overlay"/>
|
||||
|
||||
<flyoutpanel id="about-flyoutpanel" headertext="&aboutHeader.title;">
|
||||
<html:div id="overlay-back" class="overlay-button"
|
||||
observes="cmd_back" onclick="CommandUpdater.doCommand('cmd_back');"></html:div>
|
||||
<html:div id="overlay-plus" class="overlay-button"
|
||||
observes="cmd_back" onclick="CommandUpdater.doCommand('cmd_newTab');"></html:div>
|
||||
|
||||
<flyoutpanel id="about-flyoutpanel" headertext="&aboutHeader.title;">
|
||||
<label id="about-product-label" value="&aboutHeader.product.label;"/>
|
||||
<label value="&aboutHeader.company.label;"/>
|
||||
#expand <label id="about-version-label">__MOZ_APP_VERSION__</label>
|
||||
|
@ -425,7 +430,6 @@
|
|||
</menulist>
|
||||
</setting>
|
||||
</settings>
|
||||
<setting pref="browser.tabs.tabsOnly" title="&optionsHeader.tabs.title;" type="bool"/>
|
||||
<settings id="prefs-charencoding" label="&optionsHeader.char.title;">
|
||||
<setting pref="browser.menu.showCharacterEncoding" title="&optionsHeader.char.options.label;" type="bool"/>
|
||||
</settings>
|
||||
|
@ -681,9 +685,4 @@
|
|||
</hbox>
|
||||
</stack>
|
||||
|
||||
<html:div id="overlay-back" class="overlay-button"
|
||||
observes="cmd_back" onclick="CommandUpdater.doCommand('cmd_back');"></html:div>
|
||||
<html:div id="overlay-plus" class="overlay-button"
|
||||
observes="cmd_back" onclick="CommandUpdater.doCommand('cmd_newTab');"></html:div>
|
||||
|
||||
</window>
|
||||
|
|
|
@ -185,7 +185,7 @@ var TouchModule = {
|
|||
// a edge ui event when we get the contextmenu event.
|
||||
if (this._treatMouseAsTouch) {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUIGesture", true, false);
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -88,6 +88,6 @@ gTests.push({
|
|||
|
||||
function doEdgeUIGesture() {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUIGesture", true, false);
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
window.dispatchEvent(event);
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ function fireAppBarDisplayEvent()
|
|||
{
|
||||
let promise = waitForEvent(Elements.tray, "transitionend");
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("MozEdgeUIGesture", true, false);
|
||||
event.initEvent("MozEdgeUICompleted", true, false);
|
||||
gWindow.dispatchEvent(event);
|
||||
purgeEventQueue();
|
||||
return promise;
|
||||
|
@ -735,4 +735,4 @@ function stubMethod(aObj, aMethod) {
|
|||
};
|
||||
aObj[aMethod] = func;
|
||||
return func;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
<!ENTITY optionsHeader.homepage.title "When &brandShortName; starts, show">
|
||||
<!ENTITY optionsHeader.homepage.startPage.button "start page">
|
||||
<!ENTITY optionsHeader.homepage.sessionRestore.button "tabs from last time">
|
||||
<!ENTITY optionsHeader.tabs.title "Always Show Tabs">
|
||||
<!ENTITY optionsHeader.char.title "Character Encoding">
|
||||
<!ENTITY optionsHeader.char.autodetect.label "Auto-detect">
|
||||
<!ENTITY optionsHeader.char.options.label "Show encoding options on the App Bar">
|
||||
|
|
|
@ -51,12 +51,10 @@ pref("toolkit.browser.cacheRatioHeight", 3000);
|
|||
// expires.
|
||||
pref("toolkit.browser.contentViewExpire", 3000);
|
||||
|
||||
|
||||
pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
|
||||
pref("browser.chromeURL", "chrome://browser/content/");
|
||||
|
||||
// When true, always show the tab strip and use desktop-style tabs (no thumbnails)
|
||||
pref("browser.tabs.tabsOnly", false);
|
||||
|
||||
pref("browser.tabs.remote", false);
|
||||
|
||||
// Telemetry
|
||||
|
|
|
@ -48,9 +48,7 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
/* in non-tabsonly mode the navigation bar and tab tray float over content. In
|
||||
tabsonly mode they are always visible and offset content. */
|
||||
#tray:not([tabsonly]) {
|
||||
#tray {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
|
@ -144,16 +142,16 @@
|
|||
}
|
||||
}
|
||||
|
||||
#tray:not([tabsonly]) .documenttab-container {
|
||||
.documenttab-container {
|
||||
animation: open-documenttab 0.4s ease-out;
|
||||
}
|
||||
|
||||
#tray:not([tabsonly]) documenttab[closing] > .documenttab-container {
|
||||
documenttab[closing] > .documenttab-container {
|
||||
animation: close-documenttab 0.4s ease-out;
|
||||
animation-fill-mode: forwards;
|
||||
}
|
||||
|
||||
#tray:not([tabsonly]) .documenttab-favicon {
|
||||
.documenttab-favicon {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
|
@ -203,87 +201,6 @@ documenttab[selected] .documenttab-selection {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#tray[tabsonly] {
|
||||
transform: none !important;
|
||||
}
|
||||
|
||||
#tray[tabsonly] #tabs {
|
||||
-moz-padding-start: @metro_spacing_small@;
|
||||
}
|
||||
|
||||
#tray[tabsonly] #tabs-controls {
|
||||
-moz-box-align: center;
|
||||
-moz-box-orient: horizontal;
|
||||
-moz-box-pack: end;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#tray[tabsonly] #tabs-controls toolbarbutton {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab {
|
||||
height: @toolbar_height@;
|
||||
margin: 0 -@tab_compression@;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab:first-child {
|
||||
-moz-margin-start: 0;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab:last-child {
|
||||
-moz-margin-end: 0;
|
||||
}
|
||||
|
||||
#tray[tabsonly] .documenttab-thumbnail,
|
||||
#tray[tabsonly] .documenttab-selection,
|
||||
#tray[tabsonly] .documenttab-crop {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#tray[tabsonly] .documenttab-container {
|
||||
display: -moz-box;
|
||||
-moz-box-orient: horizontal;
|
||||
-moz-box-align: center;
|
||||
padding: 0 @tab_spacing@;
|
||||
}
|
||||
|
||||
#tray[tabsonly] .documenttab-favicon {
|
||||
-moz-margin-start: @metro_spacing_normal@;
|
||||
-moz-margin-end: @metro_spacing_snormal@;
|
||||
}
|
||||
|
||||
#tray[tabsonly] .documenttab-title {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: auto;
|
||||
background: 0 none;
|
||||
opacity: 1;
|
||||
box-shadow: none;
|
||||
width: @tab_inner_width@;
|
||||
}
|
||||
|
||||
#tray[tabsonly] .documenttab-close {
|
||||
list-style-image: url("chrome://browser/skin/images/closetab-tab.png");
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab[selected=true] {
|
||||
background-color: @panel_light_color@;
|
||||
background-image: url("chrome://browser/skin/images/tab-selection-left.png"),
|
||||
url("chrome://browser/skin/images/tab-selection-right.png"),
|
||||
@panel_light_background@;
|
||||
background-position: left top, right top, center center;
|
||||
background-repeat: no-repeat, no-repeat, repeat;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab[selected=true] .documenttab-close {
|
||||
list-style-image: url("chrome://browser/skin/images/closetab-tabselected.png");
|
||||
}
|
||||
|
||||
#page,
|
||||
.selection-overlay {
|
||||
-moz-stack-sizing: ignore;
|
||||
|
@ -301,10 +218,6 @@ documenttab[selected] .documenttab-selection {
|
|||
display: none;
|
||||
}
|
||||
|
||||
#tray[tabsonly] documenttab[selected=true] .documenttab-title {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#tabs-controls {
|
||||
margin-top: @metro_spacing_small@;
|
||||
-moz-box-align: start;
|
||||
|
@ -968,34 +881,45 @@ setting[type="radio"] > vbox {
|
|||
background-position: left 6px center;
|
||||
}
|
||||
|
||||
#stack[fullscreen] > .overlay-button,
|
||||
#appbar[visible] ~ .overlay-button,
|
||||
.overlay-button[disabled] {
|
||||
box-shadow: none;
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#stack[fullscreen] > #overlay-back:-moz-locale-dir(ltr),
|
||||
#stack[fullscreen] > #overlay-plus:-moz-locale-dir(rtl),
|
||||
#appbar[visible] ~ #overlay-back:-moz-locale-dir(ltr),
|
||||
#appbar[visible] ~ #overlay-plus:-moz-locale-dir(rtl),
|
||||
#overlay-back[disabled]:-moz-locale-dir(ltr),
|
||||
#overlay-plus[disabled]:-moz-locale-dir(rtl) {
|
||||
transform: translateX(-60px);
|
||||
}
|
||||
|
||||
#stack[fullscreen] > #overlay-plus:-moz-locale-dir(ltr),
|
||||
#stack[fullscreen] > #overlay-back:-moz-locale-dir(rtl),
|
||||
#appbar[visible] ~ #overlay-plus:-moz-locale-dir(ltr),
|
||||
#appbar[visible] ~ #overlay-back:-moz-locale-dir(rtl),
|
||||
#overlay-plus[disabled]:-moz-locale-dir(ltr),
|
||||
#overlay-back[disabled]:-moz-locale-dir(rtl) {
|
||||
transform: translateX(60px);
|
||||
}
|
||||
|
||||
.overlay-button:not([disabled]):hover {
|
||||
.overlay-button:hover {
|
||||
background-color: hsla(210,30%,10%,.4);
|
||||
background-size: 90px;
|
||||
border-color: hsla(0,0%,100%,.9);
|
||||
}
|
||||
|
||||
#overlay-back:not([disabled]):-moz-locale-dir(ltr):hover,
|
||||
#overlay-plus:not([disabled]):-moz-locale-dir(rtl):hover {
|
||||
#overlay-back:-moz-locale-dir(ltr):hover,
|
||||
#overlay-plus:-moz-locale-dir(rtl):hover {
|
||||
background-position: right 12px center;
|
||||
transform: translateX(40px) scale(1.2);
|
||||
}
|
||||
|
||||
#overlay-plus:not([disabled]):-moz-locale-dir(ltr):hover,
|
||||
#overlay-back:not([disabled]):-moz-locale-dir(rtl):hover {
|
||||
#overlay-plus:-moz-locale-dir(ltr):hover,
|
||||
#overlay-back:-moz-locale-dir(rtl):hover {
|
||||
background-position: left 12px center;
|
||||
transform: translateX(-40px) scale(1.2);
|
||||
}
|
||||
|
|
|
@ -23,27 +23,18 @@
|
|||
%define tabs_height 178px
|
||||
|
||||
%define progress_height 5px
|
||||
%define progress_start_color #0095dd
|
||||
%define progress_end_color #97cbff
|
||||
|
||||
%define metro_orange #FF8000
|
||||
|
||||
%define label_height 30px
|
||||
|
||||
%define thumbnail_width 232px
|
||||
%define thumbnail_height 148px
|
||||
|
||||
%define tile_border_color #dbdcde
|
||||
%define tile_width 200px
|
||||
|
||||
%define tab_inner_width 180px
|
||||
%define tab_spacing 45px;
|
||||
%define tab_compression 40px;
|
||||
|
||||
%define scroller_thickness 4px
|
||||
%define scroller_minimum 8px
|
||||
|
||||
%define metro_spacing_xxsmall 2px
|
||||
%define metro_spacing_xsmall 3px
|
||||
%define metro_spacing_small 5px
|
||||
%define metro_spacing_snormal 10px
|
||||
|
@ -52,7 +43,6 @@
|
|||
%define metro_spacing_xxnormal 25px
|
||||
%define metro_spacing_xxxnormal 30px
|
||||
%define metro_spacing_large 40px
|
||||
%define metro_spacing_xlarge 45px
|
||||
|
||||
%define metro_border_thin 1px
|
||||
%define metro_border_thick 2px
|
||||
|
@ -63,58 +53,30 @@
|
|||
%define metro_font_large 25px
|
||||
%define metro_font_xlarge 45px
|
||||
|
||||
%define font_xlarge 32px
|
||||
%define font_xnormal 17px
|
||||
%define font_normal 16px
|
||||
%define font_snormal 15px
|
||||
%define font_small 12px
|
||||
%define font_xsmall 11px
|
||||
%define font_tiny 9px
|
||||
%define font_xtiny 8px
|
||||
%define font_xxtiny 7px
|
||||
|
||||
%define touch_row 47px
|
||||
%define touch_button_xlarge 48px
|
||||
%define touch_button_large 43px
|
||||
%define touch_button_small 37px
|
||||
%define touch_action_minwidth 133px
|
||||
%define touch_normal 43px
|
||||
%define touch_small 36px
|
||||
|
||||
%define margin_context_popup 21px
|
||||
%define margin_large 16px
|
||||
%define margin_xxxnormal 11px
|
||||
%define margin_xnormal 7px
|
||||
%define margin_normal 5px
|
||||
%define margin_snormal 4px
|
||||
%define margin_small 3px
|
||||
%define margin_tiny 1px
|
||||
%define margin_xtiny 1px
|
||||
|
||||
%define padding_xlarge 21px
|
||||
%define padding_large 16px
|
||||
%define padding_xxxnormal 11px
|
||||
%define padding_xxnormal 8px
|
||||
%define padding_xnormal 7px
|
||||
%define padding_normal 5px
|
||||
%define padding_snormal 4px
|
||||
%define padding_small 3px
|
||||
%define padding_xsmall 1px
|
||||
%define padding_tiny 1px
|
||||
|
||||
%define border_width_xxlarge 4px
|
||||
%define border_width_xlarge 3px
|
||||
%define border_width_large 2px
|
||||
%define border_width_small 1px
|
||||
%define border_width_tiny 1px
|
||||
|
||||
%define border_radius_normal 5px
|
||||
%define border_radius_small 4px
|
||||
%define border_radius_xsmall 2px
|
||||
%define border_radius_tiny 1px
|
||||
|
||||
%define shadow_width_xlarge 7px
|
||||
%define shadow_width_large 4px
|
||||
%define shadow_width_small 1px
|
||||
|
||||
% easing function and duration for animations match winJS showPanel method
|
||||
|
|
Двоичные данные
browser/metro/theme/images/closetab-tab.png
Двоичные данные
browser/metro/theme/images/closetab-tab.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.2 KiB |
Двоичные данные
browser/metro/theme/images/closetab-tabselected.png
Двоичные данные
browser/metro/theme/images/closetab-tabselected.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 1.3 KiB |
Двоичные данные
browser/metro/theme/images/tab-selection-left.png
Двоичные данные
browser/metro/theme/images/tab-selection-left.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 9.0 KiB |
Двоичные данные
browser/metro/theme/images/tab-selection-right.png
Двоичные данные
browser/metro/theme/images/tab-selection-right.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 9.1 KiB |
|
@ -28,13 +28,9 @@ chrome.jar:
|
|||
skin/images/reload.png (images/reload.png)
|
||||
skin/images/newtab-default.png (images/newtab-default.png)
|
||||
skin/images/closetab-default.png (images/closetab-default.png)
|
||||
skin/images/closetab-tab.png (images/closetab-tab.png)
|
||||
skin/images/closetab-tabselected.png (images/closetab-tabselected.png)
|
||||
skin/images/tab-arrows.png (images/tab-arrows.png)
|
||||
skin/images/tab-crop.png (images/tab-crop.png)
|
||||
skin/images/tab-overlay.png (images/tab-overlay.png)
|
||||
skin/images/tab-selection-left.png (images/tab-selection-left.png)
|
||||
skin/images/tab-selection-right.png (images/tab-selection-right.png)
|
||||
skin/images/identity-icons-generic.png (images/identity-icons-generic.png)
|
||||
skin/images/identity-icons-https-ev.png (images/identity-icons-https-ev.png)
|
||||
skin/images/identity-icons-https-mixed.png (images/identity-icons-https-mixed.png)
|
||||
|
|
|
@ -105,7 +105,7 @@ class RemoteAutomation(Automation):
|
|||
#
|
||||
# -> java.lang.NullPointerException at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)
|
||||
found_exception = True
|
||||
logre = re.compile(r".*\):\s(.*)")
|
||||
logre = re.compile(r".*\): \t?(.*)")
|
||||
m = logre.search(logcat[i+1])
|
||||
if m and m.group(1):
|
||||
top_frame = m.group(1)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
. "$topsrcdir/build/mozconfig.common"
|
||||
|
||||
CC=/tools/gcc-4.5-0moz3/bin/gcc
|
||||
CXX=/tools/gcc-4.5-0moz3/bin/g++
|
||||
CC="/tools/gcc-4.7.2-0moz1/bin/gcc"
|
||||
CXX="/tools/gcc-4.7.2-0moz1/bin/g++"
|
||||
|
|
|
@ -13,7 +13,7 @@ interface nsIDocShell;
|
|||
* Describes an XPCOM component used to model an enforce CSPs.
|
||||
*/
|
||||
|
||||
[scriptable, uuid(91E1F257-914C-4D4F-902C-F67F772839AB)]
|
||||
[scriptable, uuid(230b126d-afc3-4588-9794-3e135594d626)]
|
||||
interface nsIContentSecurityPolicy : nsISupports
|
||||
{
|
||||
|
||||
|
@ -62,6 +62,21 @@ interface nsIContentSecurityPolicy : nsISupports
|
|||
*/
|
||||
boolean getAllowsEval(out boolean shouldReportViolation);
|
||||
|
||||
/**
|
||||
* Whether this policy allows in-page styles.
|
||||
* This includes <style> tags with text content and style="" attributes in
|
||||
* HTML elements.
|
||||
* @param shouldReportViolation
|
||||
* Whether or not the use of eval should be reported.
|
||||
* This function always returns "true" for report-only policies, but when
|
||||
* the report-only policy is violated, shouldReportViolation is true as
|
||||
* well.
|
||||
* @return
|
||||
* Whether or not the effects of the eval call should be allowed
|
||||
* (block the call if false).
|
||||
*/
|
||||
boolean getAllowsInlineStyle(out boolean shouldReportViolation);
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
|
@ -82,6 +97,7 @@ interface nsIContentSecurityPolicy : nsISupports
|
|||
|
||||
const unsigned short VIOLATION_TYPE_INLINE_SCRIPT = 1;
|
||||
const unsigned short VIOLATION_TYPE_EVAL = 2;
|
||||
const unsigned short VIOLATION_TYPE_INLINE_STYLE = 3;
|
||||
|
||||
/**
|
||||
* Manually triggers violation report sending given a URI and reason.
|
||||
|
|
|
@ -181,6 +181,9 @@ this.CSPRep = function CSPRep(aSpecCompliant) {
|
|||
// Is this a 1.0 spec compliant CSPRep ?
|
||||
// Default to false if not specified.
|
||||
this._specCompliant = (aSpecCompliant !== undefined) ? aSpecCompliant : false;
|
||||
|
||||
// Only CSP 1.0 spec compliant policies block inline styles by default.
|
||||
this._allowInlineStyles = !aSpecCompliant;
|
||||
}
|
||||
|
||||
// Source directives for our original CSP implementation.
|
||||
|
@ -717,7 +720,8 @@ CSPRep.prototype = {
|
|||
}
|
||||
}
|
||||
return (this.allowsInlineScripts === that.allowsInlineScripts)
|
||||
&& (this.allowsEvalInScripts === that.allowsEvalInScripts);
|
||||
&& (this.allowsEvalInScripts === that.allowsEvalInScripts)
|
||||
&& (this.allowsInlineStyles === that.allowsInlineStyles);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -812,6 +816,9 @@ CSPRep.prototype = {
|
|||
newRep._allowInlineScripts = this.allowsInlineScripts
|
||||
&& aCSPRep.allowsInlineScripts;
|
||||
|
||||
newRep._allowInlineStyles = this.allowsInlineStyles
|
||||
&& aCSPRep.allowsInlineStyles;
|
||||
|
||||
newRep._innerWindowID = this._innerWindowID ?
|
||||
this._innerWindowID : aCSPRep._innerWindowID;
|
||||
|
||||
|
@ -872,6 +879,14 @@ CSPRep.prototype = {
|
|||
return this._allowInlineScripts;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if inline styles are enabled through the "inline-style"
|
||||
* keyword.
|
||||
*/
|
||||
get allowsInlineStyles () {
|
||||
return this._allowInlineStyles;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a warning message to the error console and web developer console.
|
||||
* @param aMsg
|
||||
|
|
|
@ -40,6 +40,7 @@ function ContentSecurityPolicy() {
|
|||
|
||||
// default options "wide open" since this policy will be intersected soon
|
||||
this._policy._allowInlineScripts = true;
|
||||
this._policy._allowInlineStyles = true;
|
||||
this._policy._allowEval = true;
|
||||
|
||||
this._request = "";
|
||||
|
@ -144,6 +145,14 @@ ContentSecurityPolicy.prototype = {
|
|||
return this._reportOnlyMode || this._policy.allowsEvalInScripts;
|
||||
},
|
||||
|
||||
getAllowsInlineStyle: function(shouldReportViolation) {
|
||||
// report it?
|
||||
shouldReportViolation.value = !this._policy.allowsInlineStyles;
|
||||
|
||||
// allow it to execute?
|
||||
return this._reportOnlyMode || this._policy.allowsInlineStyles;
|
||||
},
|
||||
|
||||
/**
|
||||
* Log policy violation on the Error Console and send a report if a report-uri
|
||||
* is present in the policy
|
||||
|
@ -163,6 +172,12 @@ ContentSecurityPolicy.prototype = {
|
|||
// is enabled, resulting in a call to this function. Therefore we need to
|
||||
// check that the policy was in fact violated before logging any violations
|
||||
switch (aViolationType) {
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_STYLE:
|
||||
if (!this._policy.allowsInlineStyles)
|
||||
this._asyncReportViolation('self',null,'inline style base restriction',
|
||||
'violated base restriction: Inline Stylesheets will not apply',
|
||||
aSourceFile, aScriptSample, aLineNum);
|
||||
break;
|
||||
case Ci.nsIContentSecurityPolicy.VIOLATION_TYPE_INLINE_SCRIPT:
|
||||
if (!this._policy.allowsInlineScripts)
|
||||
this._asyncReportViolation('self',null,'inline script base restriction',
|
||||
|
|
|
@ -22,7 +22,6 @@ extern PRLogModuleInfo* GetDataChannelLog();
|
|||
#include "nsIDOMFile.h"
|
||||
#include "nsIJSNativeInitializer.h"
|
||||
#include "nsIDOMDataChannel.h"
|
||||
#include "nsIDOMRTCPeerConnection.h"
|
||||
#include "nsIDOMMessageEvent.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
|
@ -114,11 +113,6 @@ private:
|
|||
};
|
||||
|
||||
DOMCI_DATA(DataChannel, nsDOMDataChannel)
|
||||
// A bit of a hack for RTCPeerConnection, since it doesn't have a .cpp file of
|
||||
// its own. Note that it's not castable to anything in particular other than
|
||||
// nsIDOMRTCPeerConnection, so we can just use nsIDOMRTCPeerConnection as the
|
||||
// "class".
|
||||
DOMCI_DATA(RTCPeerConnection, nsIDOMRTCPeerConnection)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMDataChannel,
|
||||
nsDOMEventTargetHelper)
|
||||
|
@ -167,10 +161,6 @@ nsDOMDataChannel::Init(nsPIDOMWindow* aDOMWindow)
|
|||
rv = CheckInnerWindowCorrectness();
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
// See bug 696085
|
||||
// We don't need to observe for window destroyed or frozen; but PeerConnection needs
|
||||
// to not allow itself to be bfcached (and get destroyed on navigation).
|
||||
|
||||
rv = nsContentUtils::GetUTFOrigin(principal,mOrigin);
|
||||
LOG(("%s: origin = %s\n",__FUNCTION__,NS_LossyConvertUTF16toASCII(mOrigin).get()));
|
||||
return rv;
|
||||
|
|
|
@ -1737,7 +1737,9 @@ GK_ATOM(onMozRotateGestureUpdate, "onMozRotateGestureUpdate")
|
|||
GK_ATOM(onMozRotateGesture, "onMozRotateGesture")
|
||||
GK_ATOM(onMozTapGesture, "onMozTapGesture")
|
||||
GK_ATOM(onMozPressTapGesture, "onMozPressTapGesture")
|
||||
GK_ATOM(onMozEdgeUIGesture, "onMozEdgeUIGesture")
|
||||
GK_ATOM(onMozEdgeUIStarted, "onMozEdgeUIStarted")
|
||||
GK_ATOM(onMozEdgeUICanceled, "onMozEdgeUICanceled")
|
||||
GK_ATOM(onMozEdgeUICompleted, "onMozEdgeUICompleted")
|
||||
|
||||
// orientation support
|
||||
GK_ATOM(ondevicemotion, "ondevicemotion")
|
||||
|
|
|
@ -2397,7 +2397,7 @@ nsINode::WrapObject(JSContext *aCx, JS::Handle<JSObject*> aScope)
|
|||
JS::Rooted<JSObject*> obj(aCx, WrapNode(aCx, aScope));
|
||||
if (obj && ChromeOnlyAccess() &&
|
||||
!nsContentUtils::IsSystemPrincipal(NodePrincipal()) &&
|
||||
xpc::AllowXBLScope(js::GetContextCompartment(aCx)))
|
||||
xpc::AllowXBLScope(js::GetObjectCompartment(obj)))
|
||||
{
|
||||
// Create a new wrapper and cache it.
|
||||
JSAutoCompartment ac(aCx, obj);
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsUnicharInputStream.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsStyleUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -366,6 +367,11 @@ nsStyleLinkElement::DoUpdateStyleSheet(nsIDocument *aOldDocument,
|
|||
nsAutoString text;
|
||||
nsContentUtils::GetNodeTextContent(thisContent, false, text);
|
||||
|
||||
if (!nsStyleUtil::CSPAllowsInlineStyle(thisContent->NodePrincipal(),
|
||||
doc->GetDocumentURI(),
|
||||
mLineNumber, text, &rv))
|
||||
return rv;
|
||||
|
||||
// Parse the style sheet.
|
||||
rv = doc->CSSLoader()->
|
||||
LoadInlineStyle(thisContent, text, mLineNumber, title, media,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "nsIDOMMutationEvent.h"
|
||||
#include "nsXULElement.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsStyleUtil.h"
|
||||
|
||||
namespace css = mozilla::css;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -235,6 +236,11 @@ nsStyledElementNotElementCSSInlineStyle::ParseStyleAttribute(const nsAString& aV
|
|||
{
|
||||
nsIDocument* doc = OwnerDoc();
|
||||
|
||||
if (!nsStyleUtil::CSPAllowsInlineStyle(NodePrincipal(),
|
||||
doc->GetDocumentURI(), 0, aValue,
|
||||
nullptr))
|
||||
return;
|
||||
|
||||
if (aForceInDataDoc ||
|
||||
!doc->IsLoadedAsData() ||
|
||||
doc->IsStaticDocument()) {
|
||||
|
|
|
@ -339,6 +339,7 @@ MOCHITEST_FILES_B = \
|
|||
test_viewport_scroll.html \
|
||||
test_CSP.html \
|
||||
file_CSP.sjs \
|
||||
file_CSP.css \
|
||||
file_CSP_main.html \
|
||||
file_CSP_main.html^headers^ \
|
||||
file_CSP_main_spec_compliant.html \
|
||||
|
@ -368,6 +369,13 @@ MOCHITEST_FILES_B = \
|
|||
file_CSP_evalscript_main_spec_compliant.html^headers^ \
|
||||
file_CSP_evalscript_main_spec_compliant_allowed.html \
|
||||
file_CSP_evalscript_main_spec_compliant_allowed.html^headers^ \
|
||||
test_CSP_inlinestyle.html \
|
||||
file_CSP_inlinestyle_main.html \
|
||||
file_CSP_inlinestyle_main.html^headers^ \
|
||||
file_CSP_inlinestyle_main_spec_compliant.html \
|
||||
file_CSP_inlinestyle_main_spec_compliant.html^headers^ \
|
||||
file_CSP_inlinestyle_main_spec_compliant_allowed.html \
|
||||
file_CSP_inlinestyle_main_spec_compliant_allowed.html^headers^ \
|
||||
file_csp_bug768029.html \
|
||||
file_csp_bug768029.sjs \
|
||||
file_csp_bug773891.html \
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Moved this CSS from an inline stylesheet to an external file when we added
|
||||
* inline-style blocking in bug 763879.
|
||||
* This test may hang if the load for this .css file is blocked due to a
|
||||
* malfunction of CSP, but should pass if the style_good test passes.
|
||||
*/
|
||||
|
||||
/* CSS font embedding tests */
|
||||
@font-face {
|
||||
font-family: "arbitrary_good";
|
||||
src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
|
||||
}
|
||||
@font-face {
|
||||
font-family: "arbitrary_bad";
|
||||
src: url('http://example.org/tests/content/base/test/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
|
||||
}
|
||||
|
||||
.div_arbitrary_good { font-family: "arbitrary_good"; }
|
||||
.div_arbitrary_bad { font-family: "arbitrary_bad"; }
|
||||
|
|
@ -21,7 +21,6 @@ function handleRequest(request, response)
|
|||
}
|
||||
|
||||
if ("content" in query) {
|
||||
response.setHeader("Content-Type", "text/html", false);
|
||||
response.write(unescape(query['content']));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
<!--
|
||||
-- The original CSP implementation predates the CSP 1.0 spec and didn't
|
||||
-- block inline styles, so when the prefixed X-Content-Security-Policy header is used,
|
||||
-- as it is for this file, inline styles should be allowed.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>CSP inline script tests</title>
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSP inline script tests</title>
|
||||
<!-- content= "div#linkstylediv { color: #0f0; }" -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<style type="text/css">
|
||||
div#inlinestylediv {
|
||||
color: #00ff00;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
|
||||
<div id='attrstylediv' style="color: #00ff00;">Attribute stylesheet test (should be green)</div>
|
||||
<div id='inlinestylediv'>Inline stylesheet test (should be green)</div>
|
||||
|
||||
<!-- tests for SMIL stuff - animations -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="100%"
|
||||
height="100px">
|
||||
|
||||
<!-- Animates XML attribute, which is mapped into style. -->
|
||||
<text id="xmlTest" x="0" y="15">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="XML"
|
||||
values="lime;green;lime" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property. -->
|
||||
<text id="cssOverrideTest" x="0" y="35">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="CSS"
|
||||
values="lime;green;lime" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property targeted via ID. -->
|
||||
<text id="cssOverrideTestById" x="0" y="55">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
</text>
|
||||
<animate xlink:href="#cssOverrideTestById"
|
||||
attributeName="fill"
|
||||
values="lime;green;lime"
|
||||
dur="2s" repeatCount="indefinite" />
|
||||
|
||||
<!-- Sets value for CSS property targeted via ID. -->
|
||||
<text id="cssSetTestById" x="0" y="75">
|
||||
This should be green since the <set> should be allowed by CSP.
|
||||
</text>
|
||||
<set xlink:href="#cssSetTestById"
|
||||
attributeName="fill"
|
||||
to="lime" />
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
X-Content-Security-Policy: default-src 'self'
|
||||
Cache-Control: no-cache
|
|
@ -0,0 +1,79 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSP inline script tests</title>
|
||||
<!-- content= "div#linkstylediv { color: #0f0; }" -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
|
||||
<!-- content= "div#modifycsstextdiv { color: #0f0; }" -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
|
||||
<script>
|
||||
function cssTest() {
|
||||
var elem = document.getElementById('csstextstylediv');
|
||||
elem.style.cssText = "color: #00FF00;";
|
||||
getComputedStyle(elem, null).color;
|
||||
|
||||
document.styleSheets[1].cssRules[0].style.cssText = "color: #00FF00;";
|
||||
elem = document.getElementById('modifycsstextdiv');
|
||||
getComputedStyle(elem, null).color;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload='cssTest()'>
|
||||
|
||||
<style type="text/css">
|
||||
div#inlinestylediv {
|
||||
color: #FF0000;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
|
||||
<div id='inlinestylediv'>Inline stylesheet test (should be black)</div>
|
||||
<div id='attrstylediv' style="color: #FF0000;">Attribute stylesheet test (should be black)</div>
|
||||
<div id='csstextstylediv'>cssText test (should be black)</div>
|
||||
<div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
|
||||
|
||||
<!-- tests for SMIL stuff - animations -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="100%"
|
||||
height="100px">
|
||||
|
||||
<!-- Animates XML attribute, which is mapped into style. -->
|
||||
<text id="xmlTest" x="0" y="15">
|
||||
This shouldn't be red since the animation should be blocked by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="XML"
|
||||
values="red;orange;red" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property. -->
|
||||
<text id="cssOverrideTest" x="0" y="35">
|
||||
This shouldn't be red since the animation should be blocked by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="CSS"
|
||||
values="red;orange;red" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property targeted via ID. -->
|
||||
<text id="cssOverrideTestById" x="0" y="55">
|
||||
This shouldn't be red since the animation should be blocked by CSP.
|
||||
</text>
|
||||
<animate xlink:href="#cssOverrideTestById"
|
||||
attributeName="fill"
|
||||
values="red;orange;red"
|
||||
dur="2s" repeatCount="indefinite" />
|
||||
|
||||
<!-- Sets value for CSS property targeted via ID. -->
|
||||
<text id="cssSetTestById" x="0" y="75">
|
||||
This shouldn't be red since the <set> should be blocked by CSP.
|
||||
</text>
|
||||
<set xlink:href="#cssSetTestById"
|
||||
attributeName="fill"
|
||||
to="red" />
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline'
|
||||
Cache-Control: no-cache
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSP inline script tests</title>
|
||||
<!-- content= "div#linkstylediv { color: #0f0; }" -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href='file_CSP.sjs?type=text/css&content=div%23linkstylediv%20%7B%20color%3A%20%230f0%3B%20%7D' />
|
||||
<!-- content= "div#modifycsstextdiv { color: #f00; }" -->
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href='file_CSP.sjs?type=text/css&content=div%23modifycsstextdiv%20%7B%20color%3A%20%23f00%3B%20%7D' />
|
||||
<script>
|
||||
function cssTest() {
|
||||
// CSSStyleDeclaration.cssText
|
||||
var elem = document.getElementById('csstextstylediv');
|
||||
elem.style.cssText = "color: #00FF00;";
|
||||
|
||||
// If I call getComputedStyle as below, this test passes as the parent page
|
||||
// correctly detects that the text is colored green - if I remove this, getComputedStyle
|
||||
// thinks the text is black when called by the parent page.
|
||||
getComputedStyle(elem, null).color;
|
||||
|
||||
document.styleSheets[1].cssRules[0].style.cssText = "color: #00FF00;";
|
||||
elem = document.getElementById('modifycsstextdiv');
|
||||
getComputedStyle(elem, null).color;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body onload='cssTest()'>
|
||||
|
||||
<style type="text/css">
|
||||
div#inlinestylediv {
|
||||
color: #00FF00;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id='linkstylediv'>Link tag (external) stylesheet test (should be green)</div>
|
||||
<div id='inlinestylediv'>Inline stylesheet test (should be green)</div>
|
||||
<div id='attrstylediv' style="color: #00FF00;">Attribute stylesheet test (should be green)</div>
|
||||
<div id='csstextstylediv'>style.cssText test (should be green)</div>
|
||||
<div id='modifycsstextdiv'> modify rule from style sheet via cssText(should be green) </div>
|
||||
|
||||
<!-- tests for SMIL stuff - animations -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="100%"
|
||||
height="100px">
|
||||
|
||||
<!-- Animates XML attribute, which is mapped into style. -->
|
||||
<text id="xmlTest" x="0" y="15">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="XML"
|
||||
values="lime;green;lime" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property. -->
|
||||
<text id="cssOverrideTest" x="0" y="35">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
|
||||
<animate attributeName="fill" attributeType="CSS"
|
||||
values="lime;green;lime" dur="2s"
|
||||
repeatCount="indefinite" />
|
||||
</text>
|
||||
|
||||
<!-- Animates override value for CSS property targeted via ID. -->
|
||||
<text id="cssOverrideTestById" x="0" y="55">
|
||||
This should be green since the animation should be allowed by CSP.
|
||||
</text>
|
||||
<animate xlink:href="#cssOverrideTestById"
|
||||
attributeName="fill"
|
||||
values="lime;green;lime"
|
||||
dur="2s" repeatCount="indefinite" />
|
||||
|
||||
<!-- Sets value for CSS property targeted via ID. -->
|
||||
<text id="cssSetTestById" x="0" y="75">
|
||||
This should be green since the <set> should be allowed by CSP.
|
||||
</text>
|
||||
<set xlink:href="#cssSetTestById"
|
||||
attributeName="fill"
|
||||
to="lime" />
|
||||
</svg>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
Content-Security-Policy: default-src 'self' ; script-src 'self' 'unsafe-inline' ; style-src 'self' 'unsafe-inline'
|
||||
Cache-Control: no-cache
|
|
@ -5,21 +5,11 @@
|
|||
<link rel='stylesheet' type='text/css'
|
||||
href='file_CSP.sjs?testid=style_good&type=text/css' />
|
||||
|
||||
<!-- Used to embed inline styles here for testing fonts, but can't do that -->
|
||||
<!-- due to bug 763879 (block inline styles). Moved these to an external, CSS -->
|
||||
<!-- file (file_CSP.css). -->
|
||||
<link rel='stylesheet' type='text/css' href='file_CSP.css' />
|
||||
|
||||
<style>
|
||||
/* CSS font embedding tests */
|
||||
@font-face {
|
||||
font-family: "arbitrary_good";
|
||||
src: url('file_CSP.sjs?testid=font_good&type=application/octet-stream');
|
||||
}
|
||||
@font-face {
|
||||
font-family: "arbitrary_bad";
|
||||
src: url('http://example.org/tests/content/base/test/file_CSP.sjs?testid=font_bad&type=application/octet-stream');
|
||||
}
|
||||
|
||||
.div_arbitrary_good { font-family: "arbitrary_good"; }
|
||||
.div_arbitrary_bad { font-family: "arbitrary_bad"; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- these should be stopped by CSP. :) -->
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Content Security Policy inline stylesheets stuff</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
|
||||
<iframe style="width:100%;height:300px;" id='cspframe'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe2'></iframe>
|
||||
<iframe style="width:100%;height:300px;" id='cspframe3'></iframe>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
var path = "/tests/content/base/test/";
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// set up and go
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
var done = 0;
|
||||
|
||||
// Our original CSP implementation does not block inline styles.
|
||||
function checkStyles(evt) {
|
||||
var cspframe = document.getElementById('cspframe');
|
||||
var color;
|
||||
|
||||
// black means the style wasn't applied. green colors are used for styles
|
||||
//expected to be applied. A color is red if a style is erroneously applied
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('linkstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (original CSP implementation) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('inlinestylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Inline Style TAG (original CSP implementation) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('attrstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Style Attribute (original CSP implementation) (' + color + ')');
|
||||
// SMIL tests
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('xmlTest',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTest',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe.contentDocument.getElementById('cssSetTestById',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
|
||||
checkIfDone();
|
||||
}
|
||||
|
||||
// When a CSP 1.0 compliant policy is specified we should block inline
|
||||
// styles applied by <style> element, style attribute, and SMIL <animate> and <set> tags
|
||||
// (when it's not explicitly allowed.)
|
||||
function checkStylesSpecCompliant(evt) {
|
||||
var cspframe = document.getElementById('cspframe2');
|
||||
var color;
|
||||
|
||||
// black means the style wasn't applied. green colors are used for styles
|
||||
//expected to be applied. A color is red if a style is erroneously applied
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('linkstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (CSP 1.0 spec compliant) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('inlinestylediv'),null)['color'];
|
||||
ok('rgb(0, 0, 0)' === color, 'Inline Style TAG (CSP 1.0 spec compliant) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('attrstylediv'),null)['color'];
|
||||
ok('rgb(0, 0, 0)' === color, 'Style Attribute (CSP 1.0 spec compliant) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('csstextstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'cssText (CSP 1.0 spec compliant) (' + color + ')');
|
||||
// SMIL tests
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('xmlTest',null))['fill'];
|
||||
ok('rgb(0, 0, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('cssOverrideTest',null))['fill'];
|
||||
ok('rgb(0, 0, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
|
||||
ok('rgb(0, 0, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('cssSetTestById',null))['fill'];
|
||||
ok('rgb(0, 0, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
|
||||
|
||||
color = window.getComputedStyle(cspframe2.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
|
||||
|
||||
checkIfDone();
|
||||
}
|
||||
|
||||
// When a CSP 1.0 compliant policy is specified we should allow inline
|
||||
// styles when it is explicitly allowed.
|
||||
function checkStylesSpecCompliantAllowed(evt) {
|
||||
var cspframe = document.getElementById('cspframe3');
|
||||
var color;
|
||||
|
||||
// black means the style wasn't applied. green colors are used for styles
|
||||
// expected to be applied. A color is red if a style is erroneously applied
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('linkstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'External Stylesheet (CSP 1.0 spec compliant, allowed) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('inlinestylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Inline Style TAG (CSP 1.0 spec compliant, allowed) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('attrstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Style Attribute (CSP 1.0 spec compliant, allowed) (' + color + ')');
|
||||
|
||||
// Note that the below test will fail if "script-src: 'unsafe-inline'" breaks,
|
||||
// since it relies on executing script to set .cssText
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('csstextstylediv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'style.cssText (CSP 1.0 spec compliant, allowed) (' + color + ')');
|
||||
// SMIL tests
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('xmlTest',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'XML Attribute styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('cssOverrideTest',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Override styling (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('cssOverrideTestById',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Override styling via ID lookup (SMIL) (' + color + ')');
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('cssSetTestById',null))['fill'];
|
||||
ok('rgb(0, 255, 0)' === color, 'CSS Set Element styling via ID lookup (SMIL) (' + color + ')');
|
||||
|
||||
color = window.getComputedStyle(cspframe3.contentDocument.getElementById('modifycsstextdiv'),null)['color'];
|
||||
ok('rgb(0, 255, 0)' === color, 'Modify loaded style sheet via cssText (' + color + ')');
|
||||
|
||||
checkIfDone();
|
||||
}
|
||||
|
||||
function checkIfDone() {
|
||||
done++;
|
||||
if (done == 3)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv(
|
||||
{'set':[["security.csp.speccompliant", true]]},
|
||||
function() {
|
||||
// save this for last so that our listeners are registered.
|
||||
// ... this loads the testbed of good and bad requests.
|
||||
document.getElementById('cspframe').src = 'file_CSP_inlinestyle_main.html';
|
||||
document.getElementById('cspframe').addEventListener('load', checkStyles, false);
|
||||
document.getElementById('cspframe2').src = 'file_CSP_inlinestyle_main_spec_compliant.html';
|
||||
document.getElementById('cspframe2').addEventListener('load', checkStylesSpecCompliant, false);
|
||||
document.getElementById('cspframe3').src = 'file_CSP_inlinestyle_main_spec_compliant_allowed.html';
|
||||
document.getElementById('cspframe3').addEventListener('load', checkStylesSpecCompliantAllowed, false);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -826,8 +826,16 @@ NON_IDL_EVENT(MozPressTapGesture,
|
|||
NS_SIMPLE_GESTURE_PRESSTAP,
|
||||
EventNameType_None,
|
||||
NS_SIMPLE_GESTURE_EVENT)
|
||||
NON_IDL_EVENT(MozEdgeUIGesture,
|
||||
NS_SIMPLE_GESTURE_EDGEUI,
|
||||
NON_IDL_EVENT(MozEdgeUIStarted,
|
||||
NS_SIMPLE_GESTURE_EDGE_STARTED,
|
||||
EventNameType_None,
|
||||
NS_SIMPLE_GESTURE_EVENT)
|
||||
NON_IDL_EVENT(MozEdgeUICanceled,
|
||||
NS_SIMPLE_GESTURE_EDGE_CANCELED,
|
||||
EventNameType_None,
|
||||
NS_SIMPLE_GESTURE_EVENT)
|
||||
NON_IDL_EVENT(MozEdgeUICompleted,
|
||||
NS_SIMPLE_GESTURE_EDGE_COMPLETED,
|
||||
EventNameType_None,
|
||||
NS_SIMPLE_GESTURE_EVENT)
|
||||
|
||||
|
@ -853,6 +861,11 @@ NON_IDL_EVENT(audioprocess,
|
|||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
|
||||
NON_IDL_EVENT(complete,
|
||||
NS_AUDIO_COMPLETE,
|
||||
EventNameType_None,
|
||||
NS_EVENT)
|
||||
|
||||
#ifdef DEFINED_FORWARDED_EVENT
|
||||
#undef DEFINED_FORWARDED_EVENT
|
||||
#undef FORWARDED_EVENT
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsDOMEventTargetHelper.h"
|
||||
#include "nsPIWindowRoot.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -43,6 +44,18 @@ static char *sPopupAllowedEvents;
|
|||
|
||||
nsDOMEvent::nsDOMEvent(mozilla::dom::EventTarget* aOwner,
|
||||
nsPresContext* aPresContext, nsEvent* aEvent)
|
||||
{
|
||||
ConstructorInit(aOwner, aPresContext, aEvent);
|
||||
}
|
||||
|
||||
nsDOMEvent::nsDOMEvent(nsPIDOMWindow* aParent)
|
||||
{
|
||||
ConstructorInit(static_cast<nsGlobalWindow *>(aParent), nullptr, nullptr);
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
void nsDOMEvent::ConstructorInit(mozilla::dom::EventTarget* aOwner,
|
||||
nsPresContext* aPresContext, nsEvent* aEvent)
|
||||
{
|
||||
SetOwner(aOwner);
|
||||
|
||||
|
|
|
@ -38,7 +38,11 @@ class nsDOMEvent : public nsDOMEventBase,
|
|||
protected:
|
||||
nsDOMEvent(mozilla::dom::EventTarget* aOwner, nsPresContext* aPresContext,
|
||||
nsEvent* aEvent);
|
||||
nsDOMEvent(nsPIDOMWindow* aWindow);
|
||||
virtual ~nsDOMEvent();
|
||||
private:
|
||||
void ConstructorInit(mozilla::dom::EventTarget* aOwner,
|
||||
nsPresContext* aPresContext, nsEvent* aEvent);
|
||||
public:
|
||||
void GetParentObject(nsIScriptGlobalObject** aParentObject)
|
||||
{
|
||||
|
|
|
@ -953,6 +953,15 @@ MediaStreamGraphImpl::RunThread()
|
|||
NS_ASSERTION(!messageQueue.IsEmpty(),
|
||||
"Shouldn't have started a graph with empty message queue!");
|
||||
|
||||
uint32_t ticksProcessed = 0;
|
||||
if (!mRealtime) {
|
||||
NS_ASSERTION(!mNonRealtimeIsRunning,
|
||||
"We should not be running in non-realtime mode already");
|
||||
NS_ASSERTION(mNonRealtimeTicksToProcess,
|
||||
"We should have a non-zero number of ticks to process");
|
||||
mNonRealtimeIsRunning = true;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
// Update mCurrentTime to the min of the playing audio times, or using the
|
||||
// wall-clock time change if no audio is playing.
|
||||
|
@ -1032,6 +1041,18 @@ MediaStreamGraphImpl::RunThread()
|
|||
allBlockedForever = false;
|
||||
}
|
||||
}
|
||||
if (!mRealtime) {
|
||||
ticksProcessed += TimeToTicksRoundDown(IdealAudioRate(), mStateComputedTime - prevComputedTime);
|
||||
// Terminate processing if we've produce enough non-realtime ticks.
|
||||
if (ticksProcessed >= mNonRealtimeTicksToProcess) {
|
||||
// Wait indefinitely when we've processed enough non-realtime ticks.
|
||||
// We'll be woken up when the graph shuts down.
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
PrepareUpdatesToMainThreadState();
|
||||
mWaitState = WAITSTATE_WAITING_INDEFINITELY;
|
||||
mMonitor.Wait(PR_INTERVAL_NO_TIMEOUT);
|
||||
}
|
||||
}
|
||||
if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
|
||||
EnsureNextIteration();
|
||||
}
|
||||
|
@ -1082,6 +1103,10 @@ MediaStreamGraphImpl::RunThread()
|
|||
messageQueue.SwapElements(mMessageQueue);
|
||||
}
|
||||
}
|
||||
|
||||
if (!mRealtime) {
|
||||
mNonRealtimeIsRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1363,14 +1388,23 @@ MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
|
|||
if (IsEmpty()) {
|
||||
if (gGraph == this) {
|
||||
gGraph = nullptr;
|
||||
delete this;
|
||||
}
|
||||
delete this;
|
||||
} else if (!mRealtime) {
|
||||
// Make sure to mark the graph as not doing non-realtime processing,
|
||||
// because otherwise AppendMessage will try to ensure that the graph
|
||||
// is running, and we will never manage to release our resources.
|
||||
mNonRealtimeProcessing = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentTaskMessageQueue.AppendElement(aMessage);
|
||||
EnsureRunInStableState();
|
||||
// Do not start running the non-realtime graph unless processing has
|
||||
// explicitly started.
|
||||
if (mRealtime || mNonRealtimeProcessing) {
|
||||
EnsureRunInStableState();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1958,12 +1992,15 @@ MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
|
|||
, mMonitor("MediaStreamGraphImpl")
|
||||
, mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
|
||||
, mWaitState(WAITSTATE_RUNNING)
|
||||
, mNonRealtimeTicksToProcess(0)
|
||||
, mNeedAnotherIteration(false)
|
||||
, mForceShutDown(false)
|
||||
, mPostedRunInStableStateEvent(false)
|
||||
, mNonRealtimeIsRunning(false)
|
||||
, mDetectedNotRunning(false)
|
||||
, mPostedRunInStableState(false)
|
||||
, mRealtime(aRealtime)
|
||||
, mNonRealtimeProcessing(false)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!gMediaStreamGraphLog) {
|
||||
|
@ -2028,7 +2065,6 @@ MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
|
|||
|
||||
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
|
||||
graph->ForceShutDown();
|
||||
delete graph;
|
||||
}
|
||||
|
||||
SourceMediaStream*
|
||||
|
@ -2071,4 +2107,19 @@ MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
|
|||
return stream;
|
||||
}
|
||||
|
||||
void
|
||||
MediaStreamGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "main thread only");
|
||||
|
||||
MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
|
||||
NS_ASSERTION(!graph->mRealtime, "non-realtime only");
|
||||
|
||||
if (graph->mNonRealtimeProcessing)
|
||||
return;
|
||||
graph->mNonRealtimeTicksToProcess = aTicksToProcess;
|
||||
graph->mNonRealtimeProcessing = true;
|
||||
graph->EnsureRunInStableState();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -939,6 +939,10 @@ public:
|
|||
* in main-thread stream state.
|
||||
*/
|
||||
int64_t GetCurrentGraphUpdateIndex() { return mGraphUpdatesSent; }
|
||||
/**
|
||||
* Start processing non-realtime for a specific number of ticks.
|
||||
*/
|
||||
void StartNonRealtimeProcessing(uint32_t aTicksToProcess);
|
||||
|
||||
/**
|
||||
* Media graph thread only.
|
||||
|
|
|
@ -480,6 +480,10 @@ public:
|
|||
WAITSTATE_WAKING_UP
|
||||
};
|
||||
WaitState mWaitState;
|
||||
/**
|
||||
* How many non-realtime ticks the graph should process.
|
||||
*/
|
||||
uint32_t mNonRealtimeTicksToProcess;
|
||||
/**
|
||||
* True when another iteration of the control loop is required.
|
||||
*/
|
||||
|
@ -493,6 +497,13 @@ public:
|
|||
* RunInStableState() and the event hasn't run yet.
|
||||
*/
|
||||
bool mPostedRunInStableStateEvent;
|
||||
/**
|
||||
* True when the non-realtime graph thread is processing, as a result of
|
||||
* a request from the main thread. When processing is finished, we post
|
||||
* a message to the main thread in order to set mNonRealtimeProcessing
|
||||
* back to false.
|
||||
*/
|
||||
bool mNonRealtimeIsRunning;
|
||||
|
||||
// Main thread only
|
||||
|
||||
|
@ -519,6 +530,11 @@ public:
|
|||
* audio.
|
||||
*/
|
||||
bool mRealtime;
|
||||
/**
|
||||
* True when a non-realtime MediaStreamGraph has started to process input. This
|
||||
* value is only accessed on the main thread.
|
||||
*/
|
||||
bool mNonRealtimeProcessing;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "base/basictypes.h"
|
||||
#include <cutils/properties.h>
|
||||
#include <stagefright/foundation/AMessage.h>
|
||||
#include <stagefright/MediaExtractor.h>
|
||||
#include <stagefright/MetaData.h>
|
||||
#include <stagefright/OMXClient.h>
|
||||
|
@ -58,7 +59,11 @@ VideoGraphicBuffer::Unlock()
|
|||
{
|
||||
android::sp<android::OmxDecoder> omxDecoder = mOmxDecoder.promote();
|
||||
if (omxDecoder.get()) {
|
||||
omxDecoder->ReleaseVideoBuffer(mMediaBuffer);
|
||||
// Post kNotifyPostReleaseVideoBuffer message to OmxDecoder via ALooper.
|
||||
// The message is delivered to OmxDecoder on ALooper thread.
|
||||
// MediaBuffer::release() could take a very long time.
|
||||
// PostReleaseVideoBuffer() prevents long time locking.
|
||||
omxDecoder->PostReleaseVideoBuffer(mMediaBuffer);
|
||||
} else {
|
||||
NS_WARNING("OmxDecoder is not present");
|
||||
if (mMediaBuffer) {
|
||||
|
@ -145,10 +150,24 @@ OmxDecoder::OmxDecoder(MediaResource *aResource,
|
|||
mAudioMetadataRead(false),
|
||||
mPaused(false)
|
||||
{
|
||||
mLooper = new ALooper;
|
||||
mLooper->setName("OmxDecoder");
|
||||
|
||||
mReflector = new AHandlerReflector<OmxDecoder>(this);
|
||||
// Register AMessage handler to ALooper.
|
||||
mLooper->registerHandler(mReflector);
|
||||
// Start ALooper thread.
|
||||
mLooper->start();
|
||||
}
|
||||
|
||||
OmxDecoder::~OmxDecoder()
|
||||
{
|
||||
{
|
||||
// Free all pending video buffers.
|
||||
Mutex::Autolock autoLock(mSeekLock);
|
||||
ReleaseAllPendingVideoBuffersLocked();
|
||||
}
|
||||
|
||||
ReleaseVideoBuffer();
|
||||
ReleaseAudioBuffer();
|
||||
|
||||
|
@ -159,6 +178,11 @@ OmxDecoder::~OmxDecoder()
|
|||
if (mAudioSource.get()) {
|
||||
mAudioSource->stop();
|
||||
}
|
||||
|
||||
// unregister AMessage handler from ALooper.
|
||||
mLooper->unregisterHandler(mReflector->id());
|
||||
// Stop ALooper thread.
|
||||
mLooper->stop();
|
||||
}
|
||||
|
||||
class AutoStopMediaSource {
|
||||
|
@ -674,32 +698,6 @@ bool OmxDecoder::ReadAudio(AudioFrame *aFrame, int64_t aSeekTimeUs)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OmxDecoder::ReleaseVideoBuffer(MediaBuffer *aBuffer)
|
||||
{
|
||||
Mutex::Autolock autoLock(mSeekLock);
|
||||
|
||||
if (!aBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mIsVideoSeeking == true) {
|
||||
mPendingVideoBuffers.push(aBuffer);
|
||||
} else {
|
||||
aBuffer->release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void OmxDecoder::ReleaseAllPendingVideoBuffersLocked()
|
||||
{
|
||||
int size = mPendingVideoBuffers.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MediaBuffer *buffer = mPendingVideoBuffers[i];
|
||||
buffer->release();
|
||||
}
|
||||
mPendingVideoBuffers.clear();
|
||||
}
|
||||
|
||||
nsresult OmxDecoder::Play() {
|
||||
if (!mPaused) {
|
||||
return NS_OK;
|
||||
|
@ -729,3 +727,51 @@ void OmxDecoder::Pause() {
|
|||
mPaused = true;
|
||||
}
|
||||
|
||||
// Called on ALooper thread.
|
||||
void OmxDecoder::onMessageReceived(const sp<AMessage> &msg)
|
||||
{
|
||||
Mutex::Autolock autoLock(mSeekLock);
|
||||
|
||||
// Free pending video buffers when OmxDecoder is not seeking video.
|
||||
// If OmxDecoder is in seeking video, the buffers are freed on seek exit.
|
||||
if (mIsVideoSeeking != true) {
|
||||
ReleaseAllPendingVideoBuffersLocked();
|
||||
}
|
||||
}
|
||||
|
||||
void OmxDecoder::PostReleaseVideoBuffer(MediaBuffer *aBuffer)
|
||||
{
|
||||
{
|
||||
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
|
||||
mPendingVideoBuffers.push(aBuffer);
|
||||
}
|
||||
|
||||
sp<AMessage> notify =
|
||||
new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id());
|
||||
// post AMessage to OmxDecoder via ALooper.
|
||||
notify->post();
|
||||
}
|
||||
|
||||
void OmxDecoder::ReleaseAllPendingVideoBuffersLocked()
|
||||
{
|
||||
Vector<MediaBuffer *> releasingVideoBuffers;
|
||||
{
|
||||
Mutex::Autolock autoLock(mPendingVideoBuffersLock);
|
||||
|
||||
int size = mPendingVideoBuffers.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MediaBuffer *buffer = mPendingVideoBuffers[i];
|
||||
releasingVideoBuffers.push(buffer);
|
||||
}
|
||||
mPendingVideoBuffers.clear();
|
||||
}
|
||||
// Free all pending video buffers without holding mPendingVideoBuffersLock.
|
||||
int size = releasingVideoBuffers.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
MediaBuffer *buffer;
|
||||
buffer = releasingVideoBuffers[i];
|
||||
buffer->release();
|
||||
}
|
||||
releasingVideoBuffers.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
#include <stagefright/foundation/ABase.h>
|
||||
#include <stagefright/foundation/AHandlerReflector.h>
|
||||
#include <stagefright/foundation/ALooper.h>
|
||||
#include <stagefright/MediaSource.h>
|
||||
#include <stagefright/DataSource.h>
|
||||
#include <stagefright/MediaSource.h>
|
||||
#include <utils/RefBase.h>
|
||||
|
@ -79,6 +83,10 @@ class OmxDecoder : public RefBase {
|
|||
kHardwareCodecsOnly = 16,
|
||||
};
|
||||
|
||||
enum {
|
||||
kNotifyPostReleaseVideoBuffer = 'noti'
|
||||
};
|
||||
|
||||
AbstractMediaDecoder *mDecoder;
|
||||
nsRefPtr<MediaResource> mResource;
|
||||
sp<GonkNativeWindow> mNativeWindow;
|
||||
|
@ -109,6 +117,9 @@ class OmxDecoder : public RefBase {
|
|||
// OMXCodec does not accept MediaBuffer during seeking. If MediaBuffer is
|
||||
// returned to OMXCodec during seeking, OMXCodec calls assert.
|
||||
Vector<MediaBuffer *> mPendingVideoBuffers;
|
||||
// The lock protects mPendingVideoBuffers.
|
||||
Mutex mPendingVideoBuffersLock;
|
||||
|
||||
// Show if OMXCodec is seeking.
|
||||
bool mIsVideoSeeking;
|
||||
// The lock protects video MediaBuffer release()'s pending operations called
|
||||
|
@ -117,6 +128,16 @@ class OmxDecoder : public RefBase {
|
|||
// Holding time should be minimum.
|
||||
Mutex mSeekLock;
|
||||
|
||||
// ALooper is a message loop used in stagefright.
|
||||
// It creates a thread for messages and handles messages in the thread.
|
||||
// ALooper is a clone of Looper in android Java.
|
||||
// http://developer.android.com/reference/android/os/Looper.html
|
||||
sp<ALooper> mLooper;
|
||||
// deliver a message to a wrapped object(OmxDecoder).
|
||||
// AHandlerReflector is similar to Handler in android Java.
|
||||
// http://developer.android.com/reference/android/os/Handler.html
|
||||
sp<AHandlerReflector<OmxDecoder> > mReflector;
|
||||
|
||||
// 'true' if a read from the audio stream was done while reading the metadata
|
||||
bool mAudioMetadataRead;
|
||||
|
||||
|
@ -175,13 +196,18 @@ public:
|
|||
return mResource;
|
||||
}
|
||||
|
||||
bool ReleaseVideoBuffer(MediaBuffer *aBuffer);
|
||||
|
||||
//Change decoder into a playing state
|
||||
nsresult Play();
|
||||
|
||||
//Change decoder into a paused state
|
||||
void Pause();
|
||||
|
||||
// Post kNotifyPostReleaseVideoBuffer message to OmxDecoder via ALooper.
|
||||
void PostReleaseVideoBuffer(MediaBuffer *aBuffer);
|
||||
// Receive a message from AHandlerReflector.
|
||||
// Called on ALooper thread.
|
||||
void onMessageReceived(const sp<AMessage> &msg);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -553,15 +553,16 @@ AudioBufferSourceNode::SendOffsetAndDurationParametersToStream(AudioNodeStream*
|
|||
}
|
||||
|
||||
void
|
||||
AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
|
||||
AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv, bool aShuttingDown)
|
||||
{
|
||||
if (!mStartCalled) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mBuffer) {
|
||||
if (!mBuffer || aShuttingDown) {
|
||||
// We don't have a buffer, so the stream is never marked as finished.
|
||||
// This can also happen if the AudioContext is being shut down.
|
||||
// Therefore we need to drop our playing ref right now.
|
||||
mPlayingRef.Drop(this);
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
duration.Construct(aDuration);
|
||||
Start(aWhen, aOffset, duration, aRv);
|
||||
}
|
||||
void Stop(double aWhen, ErrorResult& aRv);
|
||||
void Stop(double aWhen, ErrorResult& aRv, bool aShuttingDown = false);
|
||||
void NoteOff(double aWhen, ErrorResult& aRv)
|
||||
{
|
||||
Stop(aWhen, aRv);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/AudioContextBinding.h"
|
||||
#include "mozilla/dom/OfflineAudioContextBinding.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "mozilla/dom/AnalyserNode.h"
|
||||
#include "AudioDestinationNode.h"
|
||||
|
@ -44,8 +46,15 @@ NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
|
|||
|
||||
static uint8_t gWebAudioOutputKey;
|
||||
|
||||
AudioContext::AudioContext(nsPIDOMWindow* aWindow)
|
||||
: mDestination(new AudioDestinationNode(this, MediaStreamGraph::GetInstance()))
|
||||
AudioContext::AudioContext(nsPIDOMWindow* aWindow,
|
||||
bool aIsOffline,
|
||||
uint32_t aNumberOfChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate)
|
||||
: mDestination(new AudioDestinationNode(this, aIsOffline,
|
||||
aNumberOfChannels,
|
||||
aLength, aSampleRate))
|
||||
, mIsOffline(aIsOffline)
|
||||
{
|
||||
// Actually play audio
|
||||
mDestination->Stream()->AddAudioOutput(&gWebAudioOutputKey);
|
||||
|
@ -64,7 +73,11 @@ AudioContext::~AudioContext()
|
|||
JSObject*
|
||||
AudioContext::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return AudioContextBinding::Wrap(aCx, aScope, this);
|
||||
if (mIsOffline) {
|
||||
return OfflineAudioContextBinding::Wrap(aCx, aScope, this);
|
||||
} else {
|
||||
return AudioContextBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioContext>
|
||||
|
@ -76,7 +89,35 @@ AudioContext::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioContext> object = new AudioContext(window);
|
||||
nsRefPtr<AudioContext> object = new AudioContext(window, false);
|
||||
window->AddAudioContext(object);
|
||||
return object.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<AudioContext>
|
||||
AudioContext::Constructor(const GlobalObject& aGlobal,
|
||||
uint32_t aNumberOfChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.Get());
|
||||
if (!window) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aSampleRate != IdealAudioRate()) {
|
||||
// TODO: Add support for running OfflineAudioContext at other sampling rates
|
||||
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioContext> object = new AudioContext(window,
|
||||
true,
|
||||
aNumberOfChannels,
|
||||
aLength,
|
||||
aSampleRate);
|
||||
window->AddAudioContext(object);
|
||||
return object.forget();
|
||||
}
|
||||
|
@ -388,7 +429,7 @@ AudioContext::Shutdown()
|
|||
GetHashtableElements(mAudioBufferSourceNodes, sourceNodes);
|
||||
for (uint32_t i = 0; i < sourceNodes.Length(); ++i) {
|
||||
ErrorResult rv;
|
||||
sourceNodes[i]->Stop(0.0, rv);
|
||||
sourceNodes[i]->Stop(0.0, rv, true);
|
||||
}
|
||||
// Stop all script processor nodes, to make sure that they release
|
||||
// their self-references.
|
||||
|
@ -397,6 +438,11 @@ AudioContext::Shutdown()
|
|||
for (uint32_t i = 0; i < spNodes.Length(); ++i) {
|
||||
spNodes[i]->Stop();
|
||||
}
|
||||
|
||||
// For offline contexts, we can destroy the MediaStreamGraph at this point.
|
||||
if (mIsOffline) {
|
||||
mDestination->DestroyGraph();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -434,5 +480,13 @@ AudioContext::GetJSContext() const
|
|||
return scriptContext->GetNativeContext();
|
||||
}
|
||||
|
||||
void
|
||||
AudioContext::StartRendering()
|
||||
{
|
||||
MOZ_ASSERT(mIsOffline, "This should only be called on OfflineAudioContext");
|
||||
|
||||
mDestination->StartRendering();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ class DelayNode;
|
|||
class DynamicsCompressorNode;
|
||||
class GainNode;
|
||||
class GlobalObject;
|
||||
class OfflineRenderSuccessCallback;
|
||||
class PannerNode;
|
||||
class ScriptProcessorNode;
|
||||
class WaveShaperNode;
|
||||
|
@ -57,7 +58,11 @@ class WaveShaperNode;
|
|||
class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
|
||||
public EnableWebAudioCheck
|
||||
{
|
||||
explicit AudioContext(nsPIDOMWindow* aParentWindow);
|
||||
AudioContext(nsPIDOMWindow* aParentWindow,
|
||||
bool aIsOffline,
|
||||
uint32_t aNumberOfChannels = 0,
|
||||
uint32_t aLength = 0,
|
||||
float aSampleRate = 0.0f);
|
||||
~AudioContext();
|
||||
|
||||
public:
|
||||
|
@ -77,9 +82,22 @@ public:
|
|||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
using nsDOMEventTargetHelper::DispatchTrustedEvent;
|
||||
|
||||
// Constructor for regular AudioContext
|
||||
static already_AddRefed<AudioContext>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
// Constructor for offline AudioContext
|
||||
static already_AddRefed<AudioContext>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
uint32_t aNumberOfChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// AudioContext methods
|
||||
|
||||
AudioDestinationNode* Destination() const
|
||||
{
|
||||
return mDestination;
|
||||
|
@ -164,7 +182,12 @@ public:
|
|||
DecodeSuccessCallback& aSuccessCallback,
|
||||
const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);
|
||||
|
||||
// OfflineAudioContext methods
|
||||
void StartRendering();
|
||||
IMPL_EVENT_HANDLER(complete)
|
||||
|
||||
uint32_t GetRate() const { return IdealAudioRate(); }
|
||||
bool IsOffline() const { return mIsOffline; }
|
||||
|
||||
MediaStreamGraph* Graph() const;
|
||||
MediaStream* DestinationStream() const;
|
||||
|
@ -193,6 +216,7 @@ private:
|
|||
// Hashset containing all ScriptProcessorNodes in order to stop them.
|
||||
// These are all weak pointers.
|
||||
nsTHashtable<nsPtrHashKey<ScriptProcessorNode> > mScriptProcessorNodes;
|
||||
bool mIsOffline;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -9,20 +9,197 @@
|
|||
#include "AudioNodeEngine.h"
|
||||
#include "AudioNodeStream.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "OfflineAudioCompletionEvent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class OfflineDestinationNodeEngine : public AudioNodeEngine
|
||||
{
|
||||
public:
|
||||
typedef AutoFallibleTArray<nsAutoArrayPtr<float>, 2> InputChannels;
|
||||
|
||||
OfflineDestinationNodeEngine(AudioDestinationNode* aNode,
|
||||
uint32_t aNumberOfChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate)
|
||||
: AudioNodeEngine(aNode)
|
||||
, mWriteIndex(0)
|
||||
, mLength(aLength)
|
||||
, mSampleRate(aSampleRate)
|
||||
{
|
||||
// These allocations might fail if content provides a huge number of
|
||||
// channels or size, but it's OK since we'll deal with the failure
|
||||
// gracefully.
|
||||
if (mInputChannels.SetLength(aNumberOfChannels)) {
|
||||
static const fallible_t fallible = fallible_t();
|
||||
for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
|
||||
mInputChannels[i] = new(fallible) float[aLength];
|
||||
if (!mInputChannels[i]) {
|
||||
mInputChannels.Clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ProduceAudioBlock(AudioNodeStream* aStream,
|
||||
const AudioChunk& aInput,
|
||||
AudioChunk* aOutput,
|
||||
bool* aFinished) MOZ_OVERRIDE
|
||||
{
|
||||
// Do this just for the sake of political correctness; this output
|
||||
// will not go anywhere.
|
||||
*aOutput = aInput;
|
||||
|
||||
// Handle the case of allocation failure in the input buffer
|
||||
if (mInputChannels.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Record our input buffer
|
||||
MOZ_ASSERT(mWriteIndex < mLength, "How did this happen?");
|
||||
const uint32_t duration = std::min(WEBAUDIO_BLOCK_SIZE, mLength - mWriteIndex);
|
||||
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
|
||||
if (aInput.IsNull()) {
|
||||
PodZero(mInputChannels[i] + mWriteIndex, duration);
|
||||
} else {
|
||||
const float* inputBuffer = static_cast<const float*>(aInput.mChannelData[i]);
|
||||
if (duration == WEBAUDIO_BLOCK_SIZE) {
|
||||
// Use the optimized version of the copy with scale operation
|
||||
AudioBlockCopyChannelWithScale(inputBuffer, aInput.mVolume,
|
||||
mInputChannels[i] + mWriteIndex);
|
||||
} else {
|
||||
if (aInput.mVolume == 1.0f) {
|
||||
PodCopy(mInputChannels[i] + mWriteIndex, inputBuffer, duration);
|
||||
} else {
|
||||
for (uint32_t j = 0; j < duration; ++j) {
|
||||
mInputChannels[i][mWriteIndex + j] = aInput.mVolume * inputBuffer[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mWriteIndex += duration;
|
||||
|
||||
if (mWriteIndex == mLength) {
|
||||
SendBufferToMainThread(aStream);
|
||||
*aFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SendBufferToMainThread(AudioNodeStream* aStream)
|
||||
{
|
||||
class Command : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Command(AudioNodeStream* aStream,
|
||||
InputChannels& aInputChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate)
|
||||
: mStream(aStream)
|
||||
, mLength(aLength)
|
||||
, mSampleRate(aSampleRate)
|
||||
{
|
||||
mInputChannels.SwapElements(aInputChannels);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP Run()
|
||||
{
|
||||
// If it's not safe to run scripts right now, schedule this to run later
|
||||
if (!nsContentUtils::IsSafeToRunScript()) {
|
||||
nsContentUtils::AddScriptRunner(this);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioContext> context;
|
||||
{
|
||||
MutexAutoLock lock(mStream->Engine()->NodeMutex());
|
||||
AudioNode* node = mStream->Engine()->Node();
|
||||
if (node) {
|
||||
context = node->Context();
|
||||
}
|
||||
}
|
||||
if (!context) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AutoPushJSContext cx(context->GetJSContext());
|
||||
if (cx) {
|
||||
JSAutoRequest ar(cx);
|
||||
|
||||
// Create the input buffer
|
||||
nsRefPtr<AudioBuffer> renderedBuffer = new AudioBuffer(context,
|
||||
mLength,
|
||||
mSampleRate);
|
||||
if (!renderedBuffer->InitializeBuffers(mInputChannels.Length(), cx)) {
|
||||
return NS_OK;
|
||||
}
|
||||
for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
|
||||
renderedBuffer->SetRawChannelContents(cx, i, mInputChannels[i]);
|
||||
}
|
||||
|
||||
nsRefPtr<OfflineAudioCompletionEvent> event =
|
||||
new OfflineAudioCompletionEvent(context, nullptr, nullptr);
|
||||
event->InitEvent(renderedBuffer);
|
||||
context->DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<AudioNodeStream> mStream;
|
||||
InputChannels mInputChannels;
|
||||
uint32_t mLength;
|
||||
float mSampleRate;
|
||||
};
|
||||
|
||||
// Empty out the source array to make sure we don't attempt to collect
|
||||
// more input data in the future.
|
||||
NS_DispatchToMainThread(new Command(aStream, mInputChannels, mLength, mSampleRate));
|
||||
}
|
||||
|
||||
private:
|
||||
// The input to the destination node is recorded in the mInputChannels buffer.
|
||||
// When this buffer fills up with mLength frames, the buffered input is sent
|
||||
// to the main thread in order to dispatch OfflineAudioCompletionEvent.
|
||||
InputChannels mInputChannels;
|
||||
// An index representing the next offset in mInputChannels to be written to.
|
||||
uint32_t mWriteIndex;
|
||||
// How many frames the OfflineAudioContext intends to produce.
|
||||
uint32_t mLength;
|
||||
float mSampleRate;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(AudioDestinationNode, AudioNode)
|
||||
|
||||
AudioDestinationNode::AudioDestinationNode(AudioContext* aContext, MediaStreamGraph* aGraph)
|
||||
AudioDestinationNode::AudioDestinationNode(AudioContext* aContext,
|
||||
bool aIsOffline,
|
||||
uint32_t aNumberOfChannels,
|
||||
uint32_t aLength,
|
||||
float aSampleRate)
|
||||
: AudioNode(aContext,
|
||||
2,
|
||||
aIsOffline ? aNumberOfChannels : 2,
|
||||
ChannelCountMode::Explicit,
|
||||
ChannelInterpretation::Speakers)
|
||||
, mFramesToProduce(aLength)
|
||||
{
|
||||
mStream = aGraph->CreateAudioNodeStream(new AudioNodeEngine(this),
|
||||
MediaStreamGraph::EXTERNAL_STREAM);
|
||||
MediaStreamGraph* graph = aIsOffline ?
|
||||
MediaStreamGraph::CreateNonRealtimeInstance() :
|
||||
MediaStreamGraph::GetInstance();
|
||||
AudioNodeEngine* engine = aIsOffline ?
|
||||
new OfflineDestinationNodeEngine(this, aNumberOfChannels,
|
||||
aLength, aSampleRate) :
|
||||
new AudioNodeEngine(this);
|
||||
mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::DestroyGraph()
|
||||
{
|
||||
MOZ_ASSERT(Context() && Context()->IsOffline(),
|
||||
"Should only be called on a valid OfflineAudioContext");
|
||||
MediaStreamGraph::DestroyNonRealtimeInstance(mStream->Graph());
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
@ -31,5 +208,11 @@ AudioDestinationNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
|||
return AudioDestinationNodeBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
void
|
||||
AudioDestinationNode::StartRendering()
|
||||
{
|
||||
mStream->Graph()->StartNonRealtimeProcessing(mFramesToProduce);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,13 @@ class AudioContext;
|
|||
class AudioDestinationNode : public AudioNode
|
||||
{
|
||||
public:
|
||||
AudioDestinationNode(AudioContext* aContext, MediaStreamGraph* aGraph);
|
||||
// This node type knows what MediaStreamGraph to use based on
|
||||
// whether it's in offline mode.
|
||||
AudioDestinationNode(AudioContext* aContext,
|
||||
bool aIsOffline,
|
||||
uint32_t aNumberOfChannels = 0,
|
||||
uint32_t aLength = 0,
|
||||
float aSampleRate = 0.0f);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
|
@ -29,6 +35,12 @@ public:
|
|||
return 0;
|
||||
}
|
||||
|
||||
void StartRendering();
|
||||
|
||||
void DestroyGraph();
|
||||
|
||||
private:
|
||||
uint32_t mFramesToProduce;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ CPPSRCS := \
|
|||
EnableWebAudioCheck.cpp \
|
||||
GainNode.cpp \
|
||||
MediaBufferDecoder.cpp \
|
||||
OfflineAudioCompletionEvent.cpp \
|
||||
PannerNode.cpp \
|
||||
ScriptProcessorNode.cpp \
|
||||
ThreeDPoint.cpp \
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "OfflineAudioCompletionEvent.h"
|
||||
#include "mozilla/dom/OfflineAudioCompletionEventBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED_1(OfflineAudioCompletionEvent, nsDOMEvent,
|
||||
mRenderedBuffer)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OfflineAudioCompletionEvent)
|
||||
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
|
||||
NS_IMPL_RELEASE_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
|
||||
|
||||
OfflineAudioCompletionEvent::OfflineAudioCompletionEvent(AudioContext* aOwner,
|
||||
nsPresContext* aPresContext,
|
||||
nsEvent* aEvent)
|
||||
: nsDOMEvent(aOwner, aPresContext, aEvent)
|
||||
{
|
||||
SetIsDOMBinding();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
OfflineAudioCompletionEvent::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
{
|
||||
return OfflineAudioCompletionEventBinding::Wrap(aCx, aScope, this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef OfflineAudioCompletionEvent_h_
|
||||
#define OfflineAudioCompletionEvent_h_
|
||||
|
||||
#include "nsDOMEvent.h"
|
||||
#include "AudioBuffer.h"
|
||||
#include "AudioContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class OfflineAudioCompletionEvent : public nsDOMEvent,
|
||||
public EnableWebAudioCheck
|
||||
{
|
||||
public:
|
||||
OfflineAudioCompletionEvent(AudioContext* aOwner,
|
||||
nsPresContext *aPresContext,
|
||||
nsEvent *aEvent);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_FORWARD_TO_NSDOMEVENT
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OfflineAudioCompletionEvent, nsDOMEvent)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
|
||||
|
||||
void InitEvent(AudioBuffer* aRenderedBuffer)
|
||||
{
|
||||
InitEvent(NS_LITERAL_STRING("complete"), false, false);
|
||||
mRenderedBuffer = aRenderedBuffer;
|
||||
}
|
||||
|
||||
AudioBuffer* RenderedBuffer() const
|
||||
{
|
||||
return mRenderedBuffer;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AudioBuffer> mRenderedBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -34,6 +34,7 @@ EXPORTS.mozilla.dom += [
|
|||
'DynamicsCompressorNode.h',
|
||||
'EnableWebAudioCheck.h',
|
||||
'GainNode.h',
|
||||
'OfflineAudioCompletionEvent.h',
|
||||
'PannerNode.h',
|
||||
'ScriptProcessorNode.h',
|
||||
'WaveShaperNode.h',
|
||||
|
|
|
@ -58,6 +58,7 @@ MOCHITEST_FILES := \
|
|||
test_mediaDecoding.html \
|
||||
test_mixingRules.html \
|
||||
test_nodeToParamConnection.html \
|
||||
test_OfflineAudioContext.html \
|
||||
test_pannerNode.html \
|
||||
test_scriptProcessorNode.html \
|
||||
test_scriptProcessorNodeChannelCount.html \
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test OfflineAudioContext</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="webaudio.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(function() {
|
||||
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
|
||||
var ctx = new OfflineAudioContext(2, 100, (new AudioContext()).sampleRate);
|
||||
ok(ctx instanceof EventTarget, "OfflineAudioContexts must be EventTargets");
|
||||
|
||||
var buf = ctx.createBuffer(2, 100, ctx.sampleRate);
|
||||
for (var i = 0; i < 2; ++i) {
|
||||
for (var j = 0; j < 100; ++j) {
|
||||
buf.getChannelData(i)[j] = Math.sin(2 * Math.PI * 200 * j / ctx.sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
var src = ctx.createBufferSource();
|
||||
src.buffer = buf;
|
||||
src.start(0);
|
||||
src.connect(ctx.destination);
|
||||
ctx.startRendering();
|
||||
ctx.addEventListener("complete", function(e) {
|
||||
ok(e instanceof OfflineAudioCompletionEvent, "Correct event received");
|
||||
is(e.renderedBuffer.numberOfChannels, 2, "Correct expected number of buffers");
|
||||
compareBuffers(e.renderedBuffer.getChannelData(0), buf.getChannelData(0));
|
||||
compareBuffers(e.renderedBuffer.getChannelData(1), buf.getChannelData(1));
|
||||
|
||||
SpecialPowers.clearUserPref("media.webaudio.enabled");
|
||||
SimpleTest.finish();
|
||||
}, false);
|
||||
});
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -31,8 +31,11 @@ function compareBuffers(buf1, buf2,
|
|||
/*optional*/ offset,
|
||||
/*optional*/ length,
|
||||
/*optional*/ sourceOffset,
|
||||
/*optional*/ destOffset) {
|
||||
is(buf1.length, buf2.length, "Buffers must have the same length");
|
||||
/*optional*/ destOffset,
|
||||
/*optional*/ skipLengthCheck) {
|
||||
if (!skipLengthCheck) {
|
||||
is(buf1.length, buf2.length, "Buffers must have the same length");
|
||||
}
|
||||
if (length == undefined) {
|
||||
length = buf1.length - (offset || 0);
|
||||
}
|
||||
|
@ -100,48 +103,89 @@ function runTest()
|
|||
gTest.numberOfChannels = 2; // default
|
||||
}
|
||||
|
||||
var context = new AudioContext();
|
||||
if (!gTest.createExpectedBuffers) {
|
||||
// Assume that the output is silence
|
||||
var expectedBuffers = getEmptyBuffer(context, gTest.length);
|
||||
} else {
|
||||
var expectedBuffers = gTest.createExpectedBuffers(context);
|
||||
}
|
||||
if (!(expectedBuffers instanceof Array)) {
|
||||
expectedBuffers = [expectedBuffers];
|
||||
}
|
||||
var expectedFrames = 0;
|
||||
for (var i = 0; i < expectedBuffers.length; ++i) {
|
||||
is(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels,
|
||||
"Correct number of channels for expected buffer " + i);
|
||||
expectedFrames += expectedBuffers[i].length;
|
||||
}
|
||||
is(expectedFrames, gTest.length, "Correct number of expected frames");
|
||||
var testLength;
|
||||
|
||||
if (gTest.createGraphAsync) {
|
||||
gTest.createGraphAsync(context, function(nodeToInspect) {
|
||||
testOutput(nodeToInspect);
|
||||
});
|
||||
} else {
|
||||
testOutput(gTest.createGraph(context));
|
||||
function runTestOnContext(context, callback, testOutput) {
|
||||
if (!gTest.createExpectedBuffers) {
|
||||
// Assume that the output is silence
|
||||
var expectedBuffers = getEmptyBuffer(context, gTest.length);
|
||||
} else {
|
||||
var expectedBuffers = gTest.createExpectedBuffers(context);
|
||||
}
|
||||
if (!(expectedBuffers instanceof Array)) {
|
||||
expectedBuffers = [expectedBuffers];
|
||||
}
|
||||
var expectedFrames = 0;
|
||||
for (var i = 0; i < expectedBuffers.length; ++i) {
|
||||
is(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels,
|
||||
"Correct number of channels for expected buffer " + i);
|
||||
expectedFrames += expectedBuffers[i].length;
|
||||
}
|
||||
is(expectedFrames, gTest.length, "Correct number of expected frames");
|
||||
|
||||
if (gTest.createGraphAsync) {
|
||||
gTest.createGraphAsync(context, function(nodeToInspect) {
|
||||
testOutput(nodeToInspect, expectedBuffers, callback);
|
||||
});
|
||||
} else {
|
||||
testOutput(gTest.createGraph(context), expectedBuffers, callback);
|
||||
}
|
||||
}
|
||||
|
||||
function testOutput(nodeToInspect) {
|
||||
var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels);
|
||||
nodeToInspect.connect(sp);
|
||||
sp.connect(context.destination);
|
||||
sp.onaudioprocess = function(e) {
|
||||
var expectedBuffer = expectedBuffers.shift();
|
||||
is(e.inputBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
|
||||
"Correct number of input buffer channels");
|
||||
for (var i = 0; i < e.inputBuffer.numberOfChannels; ++i) {
|
||||
compareBuffers(e.inputBuffer.getChannelData(i), expectedBuffer.getChannelData(i));
|
||||
}
|
||||
if (expectedBuffers.length == 0) {
|
||||
sp.onaudioprocess = null;
|
||||
done();
|
||||
}
|
||||
};
|
||||
function testOnNormalContext(callback) {
|
||||
function testOutput(nodeToInspect, expectedBuffers, callback) {
|
||||
testLength = 0;
|
||||
var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels);
|
||||
nodeToInspect.connect(sp);
|
||||
sp.connect(context.destination);
|
||||
sp.onaudioprocess = function(e) {
|
||||
var expectedBuffer = expectedBuffers.shift();
|
||||
testLength += expectedBuffer.length;
|
||||
is(e.inputBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
|
||||
"Correct number of input buffer channels");
|
||||
for (var i = 0; i < e.inputBuffer.numberOfChannels; ++i) {
|
||||
compareBuffers(e.inputBuffer.getChannelData(i), expectedBuffer.getChannelData(i));
|
||||
}
|
||||
if (expectedBuffers.length == 0) {
|
||||
sp.onaudioprocess = null;
|
||||
callback();
|
||||
}
|
||||
};
|
||||
}
|
||||
var context = new AudioContext();
|
||||
runTestOnContext(context, callback, testOutput);
|
||||
}
|
||||
|
||||
function testOnOfflineContext(callback) {
|
||||
function testOutput(nodeToInspect, expectedBuffers, callback) {
|
||||
nodeToInspect.connect(context.destination);
|
||||
context.oncomplete = function(e) {
|
||||
var samplesSeen = 0;
|
||||
while (expectedBuffers.length) {
|
||||
var expectedBuffer = expectedBuffers.shift();
|
||||
is(e.renderedBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
|
||||
"Correct number of input buffer channels");
|
||||
for (var i = 0; i < e.renderedBuffer.numberOfChannels; ++i) {
|
||||
compareBuffers(e.renderedBuffer.getChannelData(i),
|
||||
expectedBuffer.getChannelData(i),
|
||||
undefined,
|
||||
expectedBuffer.length,
|
||||
samplesSeen,
|
||||
undefined,
|
||||
true);
|
||||
}
|
||||
samplesSeen += expectedBuffer.length;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
context.startRendering();
|
||||
}
|
||||
var context = new OfflineAudioContext(gTest.numberOfChannels, testLength, 48000);
|
||||
runTestOnContext(context, callback, testOutput);
|
||||
}
|
||||
|
||||
testOnNormalContext(function() {
|
||||
testOnOfflineContext(done);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "nsPresContext.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsStyleUtil.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
|
||||
|
@ -391,6 +392,13 @@ nsSMILCSSValueType::ValueFromString(nsCSSProperty aPropID,
|
|||
return;
|
||||
}
|
||||
|
||||
nsIDocument* doc = aTargetElement->GetCurrentDoc();
|
||||
if (doc && !nsStyleUtil::CSPAllowsInlineStyle(doc->NodePrincipal(),
|
||||
doc->GetDocumentURI(),
|
||||
0, aString, nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsStyleAnimation::Value parsedValue;
|
||||
if (ValueFromStringHelper(aPropID, aTargetElement, presContext,
|
||||
aString, parsedValue, aIsContextSensitive)) {
|
||||
|
|
|
@ -2305,6 +2305,9 @@ nsXULPrototypeElement::SetAttrAt(uint32_t aPos, const nsAString& aValue,
|
|||
nsCSSParser parser;
|
||||
|
||||
// XXX Get correct Base URI (need GetBaseURI on *prototype* element)
|
||||
// TODO: If we implement Content Security Policy for chrome documents
|
||||
// as has been discussed, the CSP should be checked here to see if
|
||||
// inline styles are allowed to be applied.
|
||||
parser.ParseStyleAttribute(aValue, aDocumentURI, aDocumentURI,
|
||||
// This is basically duplicating what
|
||||
// nsINode::NodePrincipal() does
|
||||
|
|
|
@ -336,7 +336,6 @@ using mozilla::dom::workers::ResolveWorkerClasses;
|
|||
|
||||
#ifdef MOZ_WEBRTC
|
||||
#include "nsIDOMDataChannel.h"
|
||||
#include "nsIDOMRTCPeerConnection.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
|
@ -915,8 +914,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
|
|||
#ifdef MOZ_WEBRTC
|
||||
NS_DEFINE_CLASSINFO_DATA(DataChannel, nsEventTargetSH,
|
||||
EVENTTARGET_SCRIPTABLE_FLAGS)
|
||||
NS_DEFINE_CLASSINFO_DATA(RTCPeerConnection, nsDOMGenericSH,
|
||||
DOM_DEFAULT_SCRIPTABLE_FLAGS)
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -2303,10 +2300,6 @@ nsDOMClassInfo::Init()
|
|||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataChannel)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
|
||||
DOM_CLASSINFO_MAP_BEGIN(RTCPeerConnection, nsIDOMRTCPeerConnection)
|
||||
DOM_CLASSINFO_MAP_ENTRY(nsIDOMRTCPeerConnection)
|
||||
DOM_CLASSINFO_MAP_END
|
||||
#endif
|
||||
|
||||
MOZ_STATIC_ASSERT(MOZ_ARRAY_LENGTH(sClassInfoData) == eDOMClassInfoIDCount,
|
||||
|
|
|
@ -242,5 +242,4 @@ DOMCI_CLASS(MozTimeManager)
|
|||
|
||||
#ifdef MOZ_WEBRTC
|
||||
DOMCI_CLASS(DataChannel)
|
||||
DOMCI_CLASS(RTCPeerConnection)
|
||||
#endif
|
||||
|
|
|
@ -1220,8 +1220,12 @@ nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
|
|||
msg = NS_SIMPLE_GESTURE_TAP;
|
||||
else if (aType.EqualsLiteral("MozPressTapGesture"))
|
||||
msg = NS_SIMPLE_GESTURE_PRESSTAP;
|
||||
else if (aType.EqualsLiteral("MozEdgeUIGesture"))
|
||||
msg = NS_SIMPLE_GESTURE_EDGEUI;
|
||||
else if (aType.EqualsLiteral("MozEdgeUIStarted"))
|
||||
msg = NS_SIMPLE_GESTURE_EDGE_STARTED;
|
||||
else if (aType.EqualsLiteral("MozEdgeUICanceled"))
|
||||
msg = NS_SIMPLE_GESTURE_EDGE_CANCELED;
|
||||
else if (aType.EqualsLiteral("MozEdgeUICompleted"))
|
||||
msg = NS_SIMPLE_GESTURE_EDGE_COMPLETED;
|
||||
else
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
|
|
@ -675,6 +675,17 @@ DOMInterfaces = {
|
|||
'nativeType': 'nsDOMNotifyAudioAvailableEvent',
|
||||
},
|
||||
|
||||
'OfflineAudioCompletionEvent': {
|
||||
'resultNotAddRefed': [ 'renderedBuffer' ],
|
||||
},
|
||||
|
||||
'OfflineAudioContext': {
|
||||
'nativeType': 'mozilla::dom::AudioContext',
|
||||
'implicitJSContext': [ 'createBuffer' ],
|
||||
'nativeOwnership': 'refcounted',
|
||||
'resultNotAddRefed': [ 'destination', 'listener' ],
|
||||
},
|
||||
|
||||
'PaintRequest': {
|
||||
'nativeType': 'nsPaintRequest',
|
||||
},
|
||||
|
@ -1542,6 +1553,7 @@ addExternalIface('DeviceRotationRate', headerFile='nsIDOMDeviceMotionEvent.h', n
|
|||
addExternalIface('DOMError')
|
||||
addExternalIface('CSSRuleList')
|
||||
addExternalIface('DOMStringList')
|
||||
addExternalIface('RTCDataChannel', nativeType='nsIDOMDataChannel')
|
||||
addExternalIface('File')
|
||||
addExternalIface('FileCallback', nativeType='nsIFileCallback',
|
||||
headerFile='nsIDOMHTMLCanvasElement.h')
|
||||
|
|
|
@ -8311,7 +8311,8 @@ class CGNativeMember(ClassMethod):
|
|||
# The holder is an nsRefPtr. If we're nullable and end up null,
|
||||
# the holder will be null anyway, so it's safe to just always
|
||||
# return it here.
|
||||
returnCode = "return ${holderName}.forget();"
|
||||
returnCode = ("(void)${declName}; // avoid warning. May end up not being read\n"
|
||||
"return ${holderName}.forget();")
|
||||
elif iface.isCallback():
|
||||
# The decl is an OwningNonNull or nsRefPtr, depending
|
||||
# on whether we're nullable.
|
||||
|
|
|
@ -320,7 +320,8 @@ class IDLUnresolvedIdentifier(IDLObject):
|
|||
[location])
|
||||
if name[0] == '_' and not allowDoubleUnderscore:
|
||||
name = name[1:]
|
||||
if name in ["constructor", "iterator", "toString", "toJSON"] and not allowForbidden:
|
||||
# TODO: Bug 872377, Restore "toJSON" to below list.
|
||||
if name in ["constructor", "iterator", "toString"] and not allowForbidden:
|
||||
raise WebIDLError("Cannot use reserved identifier '%s'" % (name),
|
||||
[location])
|
||||
|
||||
|
|
|
@ -18,11 +18,11 @@ if CONFIG['MOZ_B2G_BT']:
|
|||
MODULE = 'dom'
|
||||
XPIDL_MODULE = 'dom_bluetooth'
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDOMNavigatorBluetooth.idl',
|
||||
'nsIDOMBluetoothManager.idl',
|
||||
'nsIDOMBluetoothAdapter.idl',
|
||||
'nsIDOMBluetoothDevice.idl',
|
||||
'nsIDOMBluetoothDeviceEvent.idl'
|
||||
'nsIDOMBluetoothDeviceEvent.idl',
|
||||
'nsIDOMBluetoothManager.idl',
|
||||
'nsIDOMNavigatorBluetooth.idl',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom.bluetooth.ipc += [
|
||||
|
|
|
@ -47,8 +47,8 @@ XPIDL_SOURCES += [
|
|||
'nsIDOMSimpleGestureEvent.idl',
|
||||
'nsIDOMSmartCardEvent.idl',
|
||||
'nsIDOMStyleRuleChangeEvent.idl',
|
||||
'nsIDOMStyleSheetChangeEvent.idl',
|
||||
'nsIDOMStyleSheetApplicableStateChangeEvent.idl',
|
||||
'nsIDOMStyleSheetChangeEvent.idl',
|
||||
'nsIDOMTouchEvent.idl',
|
||||
'nsIDOMTransitionEvent.idl',
|
||||
'nsIDOMUIEvent.idl',
|
||||
|
|
|
@ -13,11 +13,17 @@ const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
|
|||
const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1";
|
||||
const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1";
|
||||
const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1";
|
||||
const PC_ICEEVENT_CONTRACT = "@mozilla.org/dom/rtcpeerconnectioniceevent;1";
|
||||
const MSEVENT_CONTRACT = "@mozilla.org/dom/mediastreamevent;1";
|
||||
const DCEVENT_CONTRACT = "@mozilla.org/dom/datachannelevent;1";
|
||||
|
||||
const PC_CID = Components.ID("{7cb2b368-b1ce-4560-acac-8e0dbda7d3d0}");
|
||||
const PC_ICE_CID = Components.ID("{8c5dbd70-2c8e-4ecb-a5ad-2fc919099f01}");
|
||||
const PC_SESSION_CID = Components.ID("{5f21ffd9-b73f-4ba0-a685-56b4667aaf1c}");
|
||||
const PC_CID = Components.ID("{9878b414-afaa-4176-a887-1e02b3b047c2}");
|
||||
const PC_ICE_CID = Components.ID("{02b9970c-433d-4cc2-923d-f7028ac66073}");
|
||||
const PC_SESSION_CID = Components.ID("{1775081b-b62d-4954-8ffe-a067bbf508a7}");
|
||||
const PC_MANAGER_CID = Components.ID("{7293e901-2be3-4c02-b4bd-cbef6fc24f78}");
|
||||
const PC_ICEEVENT_CID = Components.ID("{b9cd25a7-9859-4f9e-8f84-ef5181ff36c0}");
|
||||
const MSEVENT_CID = Components.ID("{a722a8a9-2290-4e99-a5ed-07b504292d08}");
|
||||
const DCEVENT_CID = Components.ID("{d5ed7fbf-01a8-4b18-af6c-861cf2aac920}");
|
||||
|
||||
// Global list of PeerConnection objects, so they can be cleaned up when
|
||||
// a page is torn down. (Maps inner window ID to an array of PC objects).
|
||||
|
@ -33,17 +39,7 @@ GlobalPCList.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.IPeerConnectionManager]),
|
||||
|
||||
classID: PC_MANAGER_CID,
|
||||
classInfo: XPCOMUtils.generateCI({classID: PC_MANAGER_CID,
|
||||
contractID: PC_MANAGER_CONTRACT,
|
||||
classDescription: "PeerConnectionManager",
|
||||
interfaces: [
|
||||
Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference,
|
||||
Ci.IPeerConnectionManager
|
||||
]}),
|
||||
|
||||
_xpcom_factory: {
|
||||
createInstance: function(outer, iid) {
|
||||
if (outer) {
|
||||
|
@ -56,9 +52,9 @@ GlobalPCList.prototype = {
|
|||
addPC: function(pc) {
|
||||
let winID = pc._winID;
|
||||
if (this._list[winID]) {
|
||||
this._list[winID].push(Components.utils.getWeakReference(pc));
|
||||
this._list[winID].push(Cu.getWeakReference(pc));
|
||||
} else {
|
||||
this._list[winID] = [Components.utils.getWeakReference(pc)];
|
||||
this._list[winID] = [Cu.getWeakReference(pc)];
|
||||
}
|
||||
this.removeNullRefs(winID);
|
||||
},
|
||||
|
@ -123,82 +119,116 @@ GlobalPCList.prototype = {
|
|||
};
|
||||
let _globalPCList = new GlobalPCList();
|
||||
|
||||
function IceCandidate(candidate) {
|
||||
this.candidate = candidate;
|
||||
this.sdpMid = null;
|
||||
this.sdpMLineIndex = null;
|
||||
function RTCIceCandidate() {
|
||||
this.candidate = this.sdpMid = this.sdpMLineIndex = null;
|
||||
}
|
||||
IceCandidate.prototype = {
|
||||
RTCIceCandidate.prototype = {
|
||||
classDescription: "mozRTCIceCandidate",
|
||||
classID: PC_ICE_CID,
|
||||
contractID: PC_ICE_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({classID: PC_ICE_CID,
|
||||
contractID: PC_ICE_CONTRACT,
|
||||
classDescription: "IceCandidate",
|
||||
interfaces: [
|
||||
Ci.nsIDOMRTCIceCandidate
|
||||
],
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT}),
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIDOMRTCIceCandidate, Ci.nsIDOMGlobalObjectConstructor
|
||||
]),
|
||||
|
||||
constructor: function(win, candidateInitDict) {
|
||||
if (this._win) {
|
||||
throw new Components.Exception("Constructor already called");
|
||||
}
|
||||
this._win = win;
|
||||
if (candidateInitDict !== undefined) {
|
||||
this.candidate = candidateInitDict.candidate || null;
|
||||
this.sdpMid = candidateInitDict.sdpMid || null;
|
||||
this.sdpMLineIndex = candidateInitDict.sdpMLineIndex === null ?
|
||||
null : candidateInitDict.sdpMLineIndex + 1;
|
||||
} else {
|
||||
this.candidate = this.sdpMid = this.sdpMLineIndex = null;
|
||||
}
|
||||
__init: function(dict) {
|
||||
this.candidate = dict.candidate;
|
||||
this.sdpMid = dict.sdpMid;
|
||||
this.sdpMLineIndex = ("sdpMLineIndex" in dict)? dict.sdpMLineIndex+1 : null;
|
||||
}
|
||||
};
|
||||
|
||||
function SessionDescription(type, sdp) {
|
||||
this.type = type;
|
||||
this.sdp = sdp;
|
||||
function RTCSessionDescription() {
|
||||
this.type = this.sdp = null;
|
||||
}
|
||||
SessionDescription.prototype = {
|
||||
RTCSessionDescription.prototype = {
|
||||
classDescription: "mozRTCSessionDescription",
|
||||
classID: PC_SESSION_CID,
|
||||
contractID: PC_SESSION_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
classInfo: XPCOMUtils.generateCI({classID: PC_SESSION_CID,
|
||||
contractID: PC_SESSION_CONTRACT,
|
||||
classDescription: "SessionDescription",
|
||||
interfaces: [
|
||||
Ci.nsIDOMRTCSessionDescription
|
||||
],
|
||||
flags: Ci.nsIClassInfo.DOM_OBJECT}),
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIDOMRTCSessionDescription, Ci.nsIDOMGlobalObjectConstructor
|
||||
]),
|
||||
|
||||
constructor: function(win, descriptionInitDict) {
|
||||
if (this._win) {
|
||||
throw new Components.Exception("Constructor already called");
|
||||
}
|
||||
this._win = win;
|
||||
if (descriptionInitDict !== undefined) {
|
||||
this.type = descriptionInitDict.type || null;
|
||||
this.sdp = descriptionInitDict.sdp || null;
|
||||
} else {
|
||||
this.type = this.sdp = null;
|
||||
}
|
||||
__init: function(dict) {
|
||||
this.type = dict.type;
|
||||
this.sdp = dict.sdp;
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return JSON.stringify({
|
||||
type: this.type, sdp: this.sdp
|
||||
});
|
||||
// Bug 863402 serializer support workaround
|
||||
toJSON: function() {
|
||||
return { type: this.type, sdp: this.sdp,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" } };
|
||||
}
|
||||
};
|
||||
|
||||
function PeerConnection() {
|
||||
function MediaStreamEvent() {
|
||||
this.type = this._stream = null;
|
||||
}
|
||||
MediaStreamEvent.prototype = {
|
||||
classDescription: "MediaStreamEvent",
|
||||
classID: MSEVENT_CID,
|
||||
contractID: MSEVENT_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
__init: function(type, dict) {
|
||||
this.type = type;
|
||||
this.__DOM_IMPL__.initEvent(type, dict.bubbles || false,
|
||||
dict.cancelable || false);
|
||||
this._stream = dict.stream;
|
||||
},
|
||||
|
||||
get stream() { return this._stream; }
|
||||
};
|
||||
|
||||
function RTCDataChannelEvent() {
|
||||
this.type = this._channel = null;
|
||||
}
|
||||
RTCDataChannelEvent.prototype = {
|
||||
classDescription: "RTCDataChannelEvent",
|
||||
classID: DCEVENT_CID,
|
||||
contractID: DCEVENT_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
__init: function(type, dict) {
|
||||
this.type = type;
|
||||
this.__DOM_IMPL__.initEvent(type, dict.bubbles || false,
|
||||
dict.cancelable || false);
|
||||
this._channel = dict.channel;
|
||||
},
|
||||
|
||||
get channel() { return this._channel; }
|
||||
};
|
||||
|
||||
function RTCPeerConnectionIceEvent() {
|
||||
this.type = this._candidate = null;
|
||||
}
|
||||
RTCPeerConnectionIceEvent.prototype = {
|
||||
classDescription: "RTCPeerConnectionIceEvent",
|
||||
classID: PC_ICEEVENT_CID,
|
||||
contractID: PC_ICEEVENT_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
__init: function(type, dict) {
|
||||
this.type = type;
|
||||
this.__DOM_IMPL__.initEvent(type, dict.bubbles || false,
|
||||
dict.cancelable || false);
|
||||
this._candidate = dict.candidate;
|
||||
},
|
||||
|
||||
get candidate() { return this._candidate; }
|
||||
};
|
||||
|
||||
function RTCPeerConnection() {
|
||||
this._queue = [];
|
||||
|
||||
this._pc = null;
|
||||
|
@ -224,40 +254,24 @@ function PeerConnection() {
|
|||
*/
|
||||
this._pending = false;
|
||||
|
||||
// Public attributes.
|
||||
this.onaddstream = null;
|
||||
this.onopen = null;
|
||||
this.onremovestream = null;
|
||||
this.onicecandidate = null;
|
||||
this.onstatechange = null;
|
||||
this.ongatheringchange = null;
|
||||
this.onicechange = null;
|
||||
// States
|
||||
this._iceGatheringState = this._iceConnectionState = "new";
|
||||
|
||||
// Data channel.
|
||||
this.ondatachannel = null;
|
||||
this.onconnection = null;
|
||||
this.onclosedconnection = null;
|
||||
// Deprecated callbacks
|
||||
this._ongatheringchange = null;
|
||||
this._onicechange = null;
|
||||
}
|
||||
PeerConnection.prototype = {
|
||||
RTCPeerConnection.prototype = {
|
||||
classDescription: "mozRTCPeerConnection",
|
||||
classID: PC_CID,
|
||||
contractID: PC_CONTRACT,
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
|
||||
Ci.nsIDOMGlobalPropertyInitializer,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
init: function(win) { this._win = win; },
|
||||
|
||||
classInfo: Cu.getDOMClassInfo("RTCPeerConnection"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIDOMRTCPeerConnection,
|
||||
Ci.nsIDOMGlobalObjectConstructor,
|
||||
Ci.nsISupportsWeakReference
|
||||
]),
|
||||
|
||||
// Constructor is an explicit function, because of nsIDOMGlobalObjectConstructor.
|
||||
constructor: function(win, rtcConfig) {
|
||||
if (!Services.prefs.getBoolPref("media.peerconnection.enabled")) {
|
||||
throw new Components.Exception("PeerConnection not enabled (did you set the pref?)");
|
||||
}
|
||||
if (this._win) {
|
||||
throw new Components.Exception("RTCPeerConnection constructor already called");
|
||||
}
|
||||
if (!rtcConfig ||
|
||||
__init: function(rtcConfig) {
|
||||
if (!rtcConfig.iceServers ||
|
||||
!Services.prefs.getBoolPref("media.peerconnection.use_document_iceservers")) {
|
||||
rtcConfig = {iceServers:
|
||||
JSON.parse(Services.prefs.getCharPref("media.peerconnection.default_iceservers"))};
|
||||
|
@ -268,6 +282,16 @@ PeerConnection.prototype = {
|
|||
throw new Components.Exception("Can't create RTCPeerConnections when the network is down");
|
||||
}
|
||||
|
||||
this.makeGetterSetterEH("onaddstream");
|
||||
this.makeGetterSetterEH("onicecandidate");
|
||||
this.makeGetterSetterEH("onnegotiationneeded");
|
||||
this.makeGetterSetterEH("onsignalingstatechange");
|
||||
this.makeGetterSetterEH("onremovestream");
|
||||
this.makeGetterSetterEH("ondatachannel");
|
||||
this.makeGetterSetterEH("onconnection");
|
||||
this.makeGetterSetterEH("onclosedconnection");
|
||||
this.makeGetterSetterEH("oniceconnectionstatechange");
|
||||
|
||||
this._pc = Cc["@mozilla.org/peerconnection;1"].
|
||||
createInstance(Ci.IPeerConnection);
|
||||
this._observer = new PeerConnectionObserver(this);
|
||||
|
@ -275,11 +299,10 @@ PeerConnection.prototype = {
|
|||
// Nothing starts until ICE gathering completes.
|
||||
this._queueOrRun({
|
||||
func: this._getPC().initialize,
|
||||
args: [this._observer, win, rtcConfig, Services.tm.currentThread],
|
||||
args: [this._observer, this._win, rtcConfig, Services.tm.currentThread],
|
||||
wait: true
|
||||
});
|
||||
|
||||
this._win = win;
|
||||
this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
|
||||
|
||||
|
@ -289,7 +312,7 @@ PeerConnection.prototype = {
|
|||
|
||||
_getPC: function() {
|
||||
if (!this._pc) {
|
||||
throw new Components.Exception("PeerConnection is gone (did you turn on Offline mode?)");
|
||||
throw new Components.Exception("RTCPeerConnection is gone (did you enter Offline mode?)");
|
||||
}
|
||||
return this._pc;
|
||||
},
|
||||
|
@ -335,19 +358,14 @@ PeerConnection.prototype = {
|
|||
* An RTCConfiguration looks like this:
|
||||
*
|
||||
* { "iceServers": [ { url:"stun:23.21.150.121" },
|
||||
* { url:"turn:user@turn.example.org", credential:"mypass"} ] }
|
||||
* { url:"turn:turn.example.org",
|
||||
* username:"jib", credential:"mypass"} ] }
|
||||
*
|
||||
* We check for basic structure and well-formed stun/turn urls, but not
|
||||
* validity of servers themselves, before passing along to C++.
|
||||
* WebIDL normalizes structure for us, so we test well-formed stun/turn urls,
|
||||
* but not validity of servers themselves, before passing along to C++.
|
||||
* ErrorMsg is passed in to detail which array-entry failed, if any.
|
||||
*/
|
||||
_mustValidateRTCConfiguration: function(rtcConfig, errorMsg) {
|
||||
function isObject(obj) {
|
||||
return obj && (typeof obj === "object");
|
||||
}
|
||||
function isArraylike(obj) {
|
||||
return isObject(obj) && ("length" in obj);
|
||||
}
|
||||
function nicerNewURI(uriStr, errorMsg) {
|
||||
let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
|
||||
try {
|
||||
|
@ -363,21 +381,13 @@ PeerConnection.prototype = {
|
|||
throw new Components.Exception(errorMsg + " - improper scheme: " + url.scheme,
|
||||
Cr.NS_ERROR_MALFORMED_URI);
|
||||
}
|
||||
if (server.credential && isObject(server.credential)) {
|
||||
throw new Components.Exception(errorMsg + " - invalid credential");
|
||||
}
|
||||
if (rtcConfig.iceServers) {
|
||||
let len = rtcConfig.iceServers.length;
|
||||
for (let i=0; i < len; i++) {
|
||||
mustValidateServer (rtcConfig.iceServers[i], errorMsg);
|
||||
}
|
||||
}
|
||||
if (!isObject(rtcConfig)) {
|
||||
throw new Components.Exception(errorMsg);
|
||||
}
|
||||
if (!isArraylike(rtcConfig.iceServers)) {
|
||||
throw new Components.Exception(errorMsg +
|
||||
" - iceServers [] property not present");
|
||||
}
|
||||
let len = rtcConfig.iceServers.length;
|
||||
for (let i=0; i < len; i++) {
|
||||
mustValidateServer (rtcConfig.iceServers[i], errorMsg);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -466,11 +476,87 @@ PeerConnection.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
dispatchEvent: function(event) {
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
},
|
||||
|
||||
// Log error message to web console and window.onerror, if present.
|
||||
reportError: function(msg, file, line) {
|
||||
this.reportMsg(msg, file, line, Ci.nsIScriptError.exceptionFlag);
|
||||
},
|
||||
|
||||
reportWarning: function(msg, file, line) {
|
||||
this.reportMsg(msg, file, line, Ci.nsIScriptError.warningFlag);
|
||||
},
|
||||
|
||||
reportMsg: function(msg, file, line, flag) {
|
||||
let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
|
||||
let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
|
||||
scriptError.initWithWindowID(msg, file, null, line, 0, flag,
|
||||
"content javascript", this._winID);
|
||||
let console = Cc["@mozilla.org/consoleservice;1"].
|
||||
getService(Ci.nsIConsoleService);
|
||||
console.logMessage(scriptError);
|
||||
|
||||
if (flag != Ci.nsIScriptError.warningFlag) {
|
||||
// Safely call onerror directly if present (necessary for testing)
|
||||
try {
|
||||
if (typeof this._win.onerror === "function") {
|
||||
this._win.onerror(msg, file, line);
|
||||
}
|
||||
} catch(e) {
|
||||
// If onerror itself throws, service it.
|
||||
try {
|
||||
let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
|
||||
scriptError.initWithWindowID(e.message, e.fileName, null, e.lineNumber,
|
||||
0, Ci.nsIScriptError.exceptionFlag,
|
||||
"content javascript",
|
||||
this._winID);
|
||||
console.logMessage(scriptError);
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getEH: function(type) {
|
||||
return this.__DOM_IMPL__.getEventHandler(type);
|
||||
},
|
||||
|
||||
setEH: function(type, handler) {
|
||||
this.__DOM_IMPL__.setEventHandler(type, handler);
|
||||
},
|
||||
|
||||
makeGetterSetterEH: function(name) {
|
||||
Object.defineProperty(this, name,
|
||||
{
|
||||
get:function() { return this.getEH(name); },
|
||||
set:function(h) { return this.setEH(name, h); }
|
||||
});
|
||||
},
|
||||
|
||||
get onicechange() { return this._onicechange; },
|
||||
get ongatheringchange() { return this._ongatheringchange; },
|
||||
|
||||
set onicechange(cb) {
|
||||
this.deprecated("onicechange");
|
||||
this._onicechange = cb;
|
||||
},
|
||||
set ongatheringchange(cb) {
|
||||
this.deprecated("ongatheringchange");
|
||||
this._ongatheringchange = cb;
|
||||
},
|
||||
|
||||
deprecated: function(name) {
|
||||
this.reportWarning(name + " is deprecated!", null, 0);
|
||||
},
|
||||
|
||||
createOffer: function(onSuccess, onError, constraints) {
|
||||
if (!constraints) {
|
||||
constraints = {};
|
||||
}
|
||||
|
||||
if (!onError) {
|
||||
this.deprecated("calling createOffer without failureCallback");
|
||||
}
|
||||
this._mustValidateConstraints(constraints, "createOffer passed invalid constraints");
|
||||
this._onCreateOfferSuccess = onSuccess;
|
||||
this._onCreateOfferFailure = onError;
|
||||
|
@ -483,6 +569,9 @@ PeerConnection.prototype = {
|
|||
},
|
||||
|
||||
_createAnswer: function(onSuccess, onError, constraints, provisional) {
|
||||
if (!onError) {
|
||||
this.deprecated("calling createAnswer without failureCallback");
|
||||
}
|
||||
this._onCreateAnswerSuccess = onSuccess;
|
||||
this._onCreateAnswerFailure = onError;
|
||||
|
||||
|
@ -538,10 +627,12 @@ PeerConnection.prototype = {
|
|||
case "answer":
|
||||
type = Ci.IPeerConnection.kActionAnswer;
|
||||
break;
|
||||
case "pranswer":
|
||||
throw new Components.Exception("pranswer not yet implemented",
|
||||
Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
default:
|
||||
throw new Components.Exception("Invalid type " + desc.type +
|
||||
" provided to setLocalDescription");
|
||||
break;
|
||||
}
|
||||
|
||||
this._queueOrRun({
|
||||
|
@ -567,10 +658,12 @@ PeerConnection.prototype = {
|
|||
case "answer":
|
||||
type = Ci.IPeerConnection.kActionAnswer;
|
||||
break;
|
||||
case "pranswer":
|
||||
throw new Components.Exception("pranswer not yet implemented",
|
||||
Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
default:
|
||||
throw new Components.Exception("Invalid type " + desc.type +
|
||||
" provided to setRemoteDescription");
|
||||
break;
|
||||
}
|
||||
|
||||
this._queueOrRun({
|
||||
|
@ -581,21 +674,17 @@ PeerConnection.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
updateIce: function(config, constraints, restart) {
|
||||
return Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
updateIce: function(config, constraints) {
|
||||
throw new Components.Exception("updateIce not yet implemented",
|
||||
Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
},
|
||||
|
||||
addIceCandidate: function(cand, onSuccess, onError) {
|
||||
if (!cand) {
|
||||
throw new Components.Exception("NULL candidate passed to addIceCandidate!");
|
||||
}
|
||||
|
||||
if (!cand.candidate || !cand.sdpMLineIndex) {
|
||||
if (!cand.candidate && !cand.sdpMLineIndex) {
|
||||
throw new Components.Exception("Invalid candidate passed to addIceCandidate!");
|
||||
}
|
||||
|
||||
this._onAddIceCandidateSuccess = onSuccess;
|
||||
this._onAddIceCandidateError = onError;
|
||||
this._onAddIceCandidateSuccess = onSuccess || null;
|
||||
this._onAddIceCandidateError = onError || null;
|
||||
|
||||
this._queueOrRun({
|
||||
func: this._getPC().addIceCandidate,
|
||||
|
@ -605,6 +694,9 @@ PeerConnection.prototype = {
|
|||
},
|
||||
|
||||
addStream: function(stream, constraints) {
|
||||
if (stream.currentTime === undefined) {
|
||||
throw new Components.Exception("Invalid stream passed to addStream!");
|
||||
}
|
||||
// TODO: Implement constraints.
|
||||
this._queueOrRun({
|
||||
func: this._getPC().addStream,
|
||||
|
@ -614,8 +706,14 @@ PeerConnection.prototype = {
|
|||
},
|
||||
|
||||
removeStream: function(stream) {
|
||||
//Bug844295: Not implemeting this functionality.
|
||||
return Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
//Bug 844295: Not implementing this functionality.
|
||||
throw new Components.Exception("removeStream not yet implemented",
|
||||
Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
},
|
||||
|
||||
getStreamById: function(id) {
|
||||
throw new Components.Exception("getStreamById not yet implemented",
|
||||
Cr.NS_ERROR_NOT_IMPLEMENTED);
|
||||
},
|
||||
|
||||
close: function() {
|
||||
|
@ -625,28 +723,37 @@ PeerConnection.prototype = {
|
|||
wait: false
|
||||
});
|
||||
this._closed = true;
|
||||
this.changeIceConnectionState("closed");
|
||||
},
|
||||
|
||||
get localStreams() {
|
||||
getLocalStreams: function() {
|
||||
this._checkClosed();
|
||||
return this._getPC().localStreams;
|
||||
},
|
||||
|
||||
get remoteStreams() {
|
||||
getRemoteStreams: function() {
|
||||
this._checkClosed();
|
||||
return this._getPC().remoteStreams;
|
||||
},
|
||||
|
||||
// Backwards-compatible attributes
|
||||
get localStreams() {
|
||||
this.deprecated("localStreams");
|
||||
return this.getLocalStreams();
|
||||
},
|
||||
get remoteStreams() {
|
||||
this.deprecated("remoteStreams");
|
||||
return this.getRemoteStreams();
|
||||
},
|
||||
|
||||
get localDescription() {
|
||||
this._checkClosed();
|
||||
let sdp = this._getPC().localDescription;
|
||||
if (sdp.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: this._localType, sdp: sdp,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" }
|
||||
};
|
||||
return new this._win.mozRTCSessionDescription({ type: this._localType,
|
||||
sdp: sdp });
|
||||
},
|
||||
|
||||
get remoteDescription() {
|
||||
|
@ -655,13 +762,25 @@ PeerConnection.prototype = {
|
|||
if (sdp.length == 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: this._remoteType, sdp: sdp,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" }
|
||||
};
|
||||
return new this._win.mozRTCSessionDescription({ type: this._remoteType,
|
||||
sdp: sdp });
|
||||
},
|
||||
|
||||
get signalingState() { return "stable"; }, // not yet implemented
|
||||
get iceGatheringState() { return this._iceGatheringState; },
|
||||
get iceConnectionState() { return this._iceConnectionState; },
|
||||
|
||||
changeIceGatheringState: function(state) {
|
||||
this._iceGatheringState = state;
|
||||
},
|
||||
|
||||
changeIceConnectionState: function(state) {
|
||||
this._iceConnectionState = state;
|
||||
this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
|
||||
},
|
||||
|
||||
get readyState() {
|
||||
this.deprecated("readyState");
|
||||
// checking for our local pc closed indication
|
||||
// before invoking the pc methods.
|
||||
if(this._closed) {
|
||||
|
@ -692,7 +811,7 @@ PeerConnection.prototype = {
|
|||
createDataChannel: function(label, dict) {
|
||||
this._checkClosed();
|
||||
if (dict == undefined) {
|
||||
dict = {};
|
||||
dict = {};
|
||||
}
|
||||
if (dict.maxRetransmitTime != undefined &&
|
||||
dict.maxRetransmitNum != undefined) {
|
||||
|
@ -765,85 +884,27 @@ PeerConnectionObserver.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.IPeerConnectionObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
dispatchEvent: function(event) {
|
||||
this._dompc.dispatchEvent(event);
|
||||
},
|
||||
|
||||
callCB: function(callback, arg) {
|
||||
if (callback) {
|
||||
try {
|
||||
callback.onCallback(arg);
|
||||
callback(arg);
|
||||
} catch(e) {
|
||||
// A content script (user-provided) callback threw an error. We don't
|
||||
// want this to take down peerconnection, but we still want the user
|
||||
// to see it, so we catch it, report it, and move on.
|
||||
//
|
||||
// We do stack parsing in two different places for different reasons:
|
||||
|
||||
var msg;
|
||||
if (e.result == Cr.NS_ERROR_XPC_JS_THREW_JS_OBJECT) {
|
||||
// TODO(jib@mozilla.com): Revisit once bug 862153 is fixed.
|
||||
//
|
||||
// The actual content script frame is unavailable due to bug 862153,
|
||||
// so users see file and line # into this file, which is not helpful.
|
||||
//
|
||||
// 1) Fix up the error message itself to differentiate between the
|
||||
// 22 places we call callCB() in this file, using plain JS stack.
|
||||
//
|
||||
// Tweak the existing NS_ERROR_XPC_JS_THREW_JS_OBJECT message:
|
||||
// -'Error: x' when calling method: [RTCPeerConCallback::onCallback]
|
||||
// +'Error: x' when calling method: [RTCPeerConCallback::onCreateOfferError]
|
||||
|
||||
let caller = Error().stack.split("\n")[1].split("@")[0];
|
||||
// caller ~= "PeerConnectionObserver.prototype.onCreateOfferError"
|
||||
|
||||
msg = e.message.replace("::onCallback", "::" + caller.split(".")[2]);
|
||||
} else {
|
||||
msg = e.message;
|
||||
}
|
||||
|
||||
// Log error message to web console and window.onerror, if present.
|
||||
//
|
||||
// 2) nsIScriptError doesn't understand the nsIStackFrame format, so
|
||||
// do the translation by extracting file and line from XPCOM stack:
|
||||
//
|
||||
// e.location ~= "JS frame :: file://.js :: RTCPCCb::onCallback :: line 1"
|
||||
|
||||
let stack = e.location.toString().split(" :: ");
|
||||
let file = stack[1];
|
||||
let line = parseInt(stack[3].split(" ")[1]);
|
||||
|
||||
let scriptErrorClass = Cc["@mozilla.org/scripterror;1"];
|
||||
let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
|
||||
scriptError.initWithWindowID(msg, file, null, line, 0,
|
||||
Ci.nsIScriptError.exceptionFlag,
|
||||
"content javascript",
|
||||
this._dompc._winID);
|
||||
let console = Cc["@mozilla.org/consoleservice;1"].
|
||||
getService(Ci.nsIConsoleService);
|
||||
console.logMessage(scriptError);
|
||||
|
||||
// Safely call onerror directly if present (necessary for testing)
|
||||
try {
|
||||
if (typeof this._dompc._win.onerror === "function") {
|
||||
this._dompc._win.onerror(msg, file, line);
|
||||
}
|
||||
} catch(e) {
|
||||
// If onerror itself throws, service it.
|
||||
try {
|
||||
let scriptError = scriptErrorClass.createInstance(Ci.nsIScriptError);
|
||||
scriptError.initWithWindowID(e.message, e.fileName, null,
|
||||
e.lineNumber, 0,
|
||||
Ci.nsIScriptError.exceptionFlag,
|
||||
"content javascript",
|
||||
this._dompc._winID);
|
||||
console.logMessage(scriptError);
|
||||
} catch(e) {}
|
||||
}
|
||||
this._dompc.reportError(e.message, e.fileName, e.lineNumber);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onCreateOfferSuccess: function(offer) {
|
||||
onCreateOfferSuccess: function(sdp) {
|
||||
this.callCB(this._dompc._onCreateOfferSuccess,
|
||||
{ type: "offer", sdp: offer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" } });
|
||||
new this._dompc._win.mozRTCSessionDescription({ type: "offer",
|
||||
sdp: sdp }));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
|
@ -852,10 +913,10 @@ PeerConnectionObserver.prototype = {
|
|||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
onCreateAnswerSuccess: function(answer) {
|
||||
onCreateAnswerSuccess: function(sdp) {
|
||||
this.callCB (this._dompc._onCreateAnswerSuccess,
|
||||
{ type: "answer", sdp: answer,
|
||||
__exposedProps__: { type: "rw", sdp: "rw" } });
|
||||
new this._dompc._win.mozRTCSessionDescription({ type: "answer",
|
||||
sdp: sdp }));
|
||||
this._dompc._executeNext();
|
||||
},
|
||||
|
||||
|
@ -919,23 +980,29 @@ PeerConnectionObserver.prototype = {
|
|||
}
|
||||
|
||||
switch (this._dompc._pc.iceState) {
|
||||
case Ci.IPeerConnection.kIceGathering:
|
||||
this.callCB(this._dompc.ongatheringchange, "gathering");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceWaiting:
|
||||
this.callCB(this._dompc.onicechange, "starting");
|
||||
this._dompc.changeIceConnectionState("completed");
|
||||
this.callCB(this._dompc.ongatheringchange, "complete");
|
||||
this.callCB(this._onicechange, "starting");
|
||||
// Now that the PC is ready to go, execute any pending operations.
|
||||
this._dompc._executeNext();
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceChecking:
|
||||
this.callCB(this._dompc.onicechange, "checking");
|
||||
this._dompc.changeIceConnectionState("checking");
|
||||
this.callCB(this._onicechange, "checking");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceGathering:
|
||||
this._dompc.changeIceGatheringState("gathering");
|
||||
this.callCB(this._ongatheringchange, "gathering");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceConnected:
|
||||
// ICE gathering complete.
|
||||
this.callCB(this._dompc.onicechange, "connected");
|
||||
this.callCB(this._dompc.ongatheringchange, "complete");
|
||||
this._dompc.changeIceConnectionState("connected");
|
||||
this.callCB(this._onicechange, "connected");
|
||||
break;
|
||||
case Ci.IPeerConnection.kIceFailed:
|
||||
this.callCB(this._dompc.onicechange, "failed");
|
||||
this._dompc.changeIceConnectionState("failed");
|
||||
this.callCB(this._onicechange, "failed");
|
||||
break;
|
||||
default:
|
||||
// Unknown state!
|
||||
|
@ -943,37 +1010,36 @@ PeerConnectionObserver.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
onAddStream: function(stream, type) {
|
||||
this.callCB(this._dompc.onaddstream,
|
||||
{ stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" } });
|
||||
onAddStream: function(stream) {
|
||||
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("addstream",
|
||||
{ stream: stream }));
|
||||
},
|
||||
|
||||
onRemoveStream: function(stream, type) {
|
||||
this.callCB(this._dompc.onremovestream,
|
||||
{ stream: stream, type: type,
|
||||
__exposedProps__: { stream: "r", type: "r" } });
|
||||
this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
|
||||
{ stream: stream }));
|
||||
},
|
||||
|
||||
foundIceCandidate: function(cand) {
|
||||
this.callCB(this._dompc.onicecandidate,
|
||||
{candidate: cand, __exposedProps__: { candidate: "rw" } });
|
||||
foundIceCandidate: function(c) {
|
||||
this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
|
||||
{ candidate: c }));
|
||||
},
|
||||
|
||||
notifyDataChannel: function(channel) {
|
||||
this.callCB(this._dompc.ondatachannel,
|
||||
{ channel: channel, __exposedProps__: { channel: "r" } });
|
||||
this.dispatchEvent(new this._dompc._win.RTCDataChannelEvent("datachannel",
|
||||
{ channel: channel }));
|
||||
},
|
||||
|
||||
notifyConnection: function() {
|
||||
this.callCB (this._dompc.onconnection);
|
||||
this.dispatchEvent(new this._dompc._win.Event("connection"));
|
||||
},
|
||||
|
||||
notifyClosedConnection: function() {
|
||||
this.callCB (this._dompc.onclosedconnection);
|
||||
this.dispatchEvent(new this._dompc._win.Event("closedconnection"));
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory(
|
||||
[GlobalPCList, IceCandidate, SessionDescription, PeerConnection]
|
||||
[GlobalPCList, RTCIceCandidate, RTCSessionDescription, RTCPeerConnection,
|
||||
RTCPeerConnectionIceEvent, MediaStreamEvent, RTCDataChannelEvent]
|
||||
);
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
component {7cb2b368-b1ce-4560-acac-8e0dbda7d3d0} PeerConnection.js
|
||||
component {8c5dbd70-2c8e-4ecb-a5ad-2fc919099f01} PeerConnection.js
|
||||
component {5f21ffd9-b73f-4ba0-a685-56b4667aaf1c} PeerConnection.js
|
||||
component {9878b414-afaa-4176-a887-1e02b3b047c2} PeerConnection.js
|
||||
component {02b9970c-433d-4cc2-923d-f7028ac66073} PeerConnection.js
|
||||
component {1775081b-b62d-4954-8ffe-a067bbf508a7} PeerConnection.js
|
||||
component {7293e901-2be3-4c02-b4bd-cbef6fc24f78} PeerConnection.js
|
||||
component {b9cd25a7-9859-4f9e-8f84-ef5181ff36c0} PeerConnection.js
|
||||
component {a722a8a9-2290-4e99-a5ed-07b504292d08} PeerConnection.js
|
||||
component {d5ed7fbf-01a8-4b18-af6c-861cf2aac920} PeerConnection.js
|
||||
|
||||
contract @mozilla.org/dom/peerconnection;1 {7cb2b368-b1ce-4560-acac-8e0dbda7d3d0}
|
||||
contract @mozilla.org/dom/rtcicecandidate;1 {8c5dbd70-2c8e-4ecb-a5ad-2fc919099f01}
|
||||
contract @mozilla.org/dom/rtcsessiondescription;1 {5f21ffd9-b73f-4ba0-a685-56b4667aaf1c}
|
||||
contract @mozilla.org/dom/peerconnection;1 {9878b414-afaa-4176-a887-1e02b3b047c2}
|
||||
contract @mozilla.org/dom/rtcicecandidate;1 {02b9970c-433d-4cc2-923d-f7028ac66073}
|
||||
contract @mozilla.org/dom/rtcsessiondescription;1 {1775081b-b62d-4954-8ffe-a067bbf508a7}
|
||||
contract @mozilla.org/dom/peerconnectionmanager;1 {7293e901-2be3-4c02-b4bd-cbef6fc24f78}
|
||||
|
||||
category JavaScript-global-constructor mozRTCPeerConnection @mozilla.org/dom/peerconnection;1
|
||||
category JavaScript-global-constructor mozRTCIceCandidate @mozilla.org/dom/rtcicecandidate;1
|
||||
category JavaScript-global-constructor mozRTCSessionDescription @mozilla.org/dom/rtcsessiondescription;1
|
||||
contract @mozilla.org/dom/rtcpeerconnectioniceevent;1 {b9cd25a7-9859-4f9e-8f84-ef5181ff36c0}
|
||||
contract @mozilla.org/dom/mediastreamevent;1 {a722a8a9-2290-4e99-a5ed-07b504292d08}
|
||||
contract @mozilla.org/dom/datachannelevent;1 {d5ed7fbf-01a8-4b18-af6c-861cf2aac920}
|
||||
|
|
|
@ -54,7 +54,7 @@ interface IPeerConnectionObserver : nsISupports
|
|||
void onStateChange(in unsigned long state);
|
||||
|
||||
/* Changes to MediaStreams */
|
||||
void onAddStream(in nsIDOMMediaStream stream, in string type);
|
||||
void onAddStream(in nsIDOMMediaStream stream);
|
||||
void onRemoveStream();
|
||||
void onAddTrack();
|
||||
void onRemoveTrack();
|
||||
|
|
|
@ -12,7 +12,6 @@ TEST_DIRS += ['tests/mochitest']
|
|||
XPIDL_SOURCES += [
|
||||
'nsIDOMMediaStream.idl',
|
||||
'nsIDOMNavigatorUserMedia.idl',
|
||||
'nsIDOMRTCPeerConnection.idl',
|
||||
'nsIMediaManager.idl',
|
||||
]
|
||||
|
||||
|
|
|
@ -1,100 +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 "nsISupports.idl"
|
||||
#include "nsIDOMMediaStream.idl"
|
||||
|
||||
interface nsIDOMDataChannel;
|
||||
|
||||
[scriptable, function, uuid(eb9c563c-3b09-4565-9317-eca96ae0c538)]
|
||||
interface RTCPeerConnectionCallback : nsISupports
|
||||
{
|
||||
void onCallback(in jsval value);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(55546efd-287b-4460-8283-0592875b890f)]
|
||||
interface RTCPeerConnectionCallbackVoid : nsISupports
|
||||
{
|
||||
void onCallback();
|
||||
};
|
||||
|
||||
[scriptable, uuid(05d7375e-b024-4951-a570-c6642105ad35)]
|
||||
interface nsIDOMRTCSessionDescription : nsISupports
|
||||
{
|
||||
attribute DOMString sdp;
|
||||
attribute DOMString type;
|
||||
};
|
||||
|
||||
[scriptable, uuid(df176474-e20a-4f42-a85b-b0414d634cf0)]
|
||||
interface nsIDOMRTCIceCandidate : nsISupports
|
||||
{
|
||||
attribute DOMString candidate;
|
||||
attribute DOMString sdpMid;
|
||||
attribute unsigned short sdpMLineIndex;
|
||||
};
|
||||
|
||||
/* See http://dev.w3.org/2011/webrtc/editor/webrtc.html */
|
||||
[scriptable, uuid(474074ab-11f9-4933-a200-8ea1a5f84e4c)]
|
||||
interface nsIDOMRTCPeerConnection : nsISupports
|
||||
{
|
||||
void createOffer(in RTCPeerConnectionCallback successCallback,
|
||||
[optional] in RTCPeerConnectionCallback failureCallback,
|
||||
[optional] in jsval constraints);
|
||||
|
||||
void createAnswer(in RTCPeerConnectionCallback successCallback,
|
||||
[optional] in RTCPeerConnectionCallback failureCallback,
|
||||
[optional] in jsval constraints,
|
||||
[optional] in bool createProvisionalAnswer);
|
||||
|
||||
void setLocalDescription(in nsIDOMRTCSessionDescription desc,
|
||||
[optional] in RTCPeerConnectionCallbackVoid successCallback,
|
||||
[optional] in RTCPeerConnectionCallback failureCallback);
|
||||
|
||||
void setRemoteDescription(in nsIDOMRTCSessionDescription desc,
|
||||
[optional] in RTCPeerConnectionCallbackVoid successCallback,
|
||||
[optional] in RTCPeerConnectionCallback failureCallback);
|
||||
|
||||
void updateIce([optional] in jsval configuration,
|
||||
[optional] in jsval constraints,
|
||||
[optional] in bool restart);
|
||||
|
||||
void addIceCandidate(in nsIDOMRTCIceCandidate candidate,
|
||||
[optional] in RTCPeerConnectionCallbackVoid successCallback,
|
||||
[optional] in RTCPeerConnectionCallback failureCallback);
|
||||
|
||||
void addStream(in nsIDOMMediaStream stream,
|
||||
[optional] in jsval constraints);
|
||||
|
||||
void removeStream(in nsIDOMMediaStream stream);
|
||||
void close();
|
||||
|
||||
/* Readonly attributes */
|
||||
readonly attribute DOMString iceState;
|
||||
readonly attribute DOMString iceGatheringState;
|
||||
|
||||
readonly attribute DOMString readyState;
|
||||
readonly attribute jsval localDescription;
|
||||
readonly attribute jsval remoteDescription;
|
||||
|
||||
readonly attribute jsval localStreams; // MediaStream[]
|
||||
readonly attribute jsval remoteStreams; // MediaStream[]
|
||||
|
||||
/* Event handlers. TODO: Use real EventTarget */
|
||||
attribute RTCPeerConnectionCallback onaddstream;
|
||||
attribute RTCPeerConnectionCallback onopen;
|
||||
attribute RTCPeerConnectionCallback onremovestream;
|
||||
attribute RTCPeerConnectionCallback onicecandidate;
|
||||
attribute RTCPeerConnectionCallback onstatechange;
|
||||
attribute RTCPeerConnectionCallback ongatheringchange;
|
||||
attribute RTCPeerConnectionCallback onicechange;
|
||||
|
||||
/* Data channels */
|
||||
nsIDOMDataChannel createDataChannel([optional] in ACString label,
|
||||
[optional] in jsval options);
|
||||
void connectDataConnection(in unsigned short localport,
|
||||
in unsigned short remoteport, [optional] in unsigned short numstreams);
|
||||
attribute RTCPeerConnectionCallback ondatachannel;
|
||||
attribute RTCPeerConnectionCallbackVoid onconnection;
|
||||
attribute RTCPeerConnectionCallbackVoid onclosedconnection;
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=837421
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 837421</title>
|
||||
<script type="application/javascript">
|
||||
function finish() {
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
|
||||
function start() {
|
||||
var o0 = mozRTCPeerConnection();
|
||||
var o1 = new mozRTCIceCandidate({"candidate":"127 15 UDP 1 stun.sipgate.net 134117531 type 2001:db8:85a3:0:0:8a2e:370:3478 rport","sdpMid":"application 3566220586 RTP/AVP 5000","sdpMLineIndex":-32767});
|
||||
o0.connectDataConnection(3478,-1);
|
||||
o0.connectDataConnection(-1,3478,2);
|
||||
var o2 = mozRTCPeerConnection();
|
||||
try {o2.addIceCandidate(o1);} catch(e) {} // bug 842075 - remove try when fixed
|
||||
o2.connectDataConnection(-1,3478,0.5);
|
||||
o2.connectDataConnection(3478,-1,0.5);
|
||||
var o3 = new mozRTCIceCandidate({"candidate":"31 2097151 IP 33554431 ::ffff:192.0.2.128 3999799469 type numb.viagenie.ca host","sdpMid":"application 1261077875 RTP/AVP 5000","sdpMLineIndex":16777215});
|
||||
o2.connectDataConnection(1,3478);
|
||||
o2.connectDataConnection(3478,1);
|
||||
try {o2.updateIce()} catch(e) {}
|
||||
try {o2.addIceCandidate(o3);} catch(e) {} // bug 842075 - remove try when fixed
|
||||
finish();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="setTimeout(start, 100)">
|
||||
</html>
|
|
@ -10,7 +10,6 @@ load 812785.html
|
|||
load 834100.html
|
||||
load 836349.html
|
||||
load 837324.html
|
||||
load 837421.html
|
||||
load 855796.html
|
||||
load 860143.html
|
||||
load 861958.html
|
||||
|
|
|
@ -558,15 +558,14 @@ PeerConnectionWrapper.prototype = {
|
|||
/**
|
||||
* Sets the local description and automatically handles the failure case.
|
||||
*
|
||||
* @param {object} sdp
|
||||
* SDP for the local description request
|
||||
* @param {object} desc
|
||||
* mozRTCSessionDescription for the local description request
|
||||
* @param {function} onSuccess
|
||||
* Callback to execute if the local description was set successfully
|
||||
*/
|
||||
setLocalDescription : function PCW_setLocalDescription(sdp, onSuccess) {
|
||||
setLocalDescription : function PCW_setLocalDescription(desc, onSuccess) {
|
||||
var self = this;
|
||||
|
||||
this._pc.setLocalDescription(sdp, function () {
|
||||
this._pc.setLocalDescription(desc, function () {
|
||||
info("Successfully set the local description for " + self.label);
|
||||
onSuccess();
|
||||
}, unexpectedCallbackAndFinish(new Error));
|
||||
|
@ -575,15 +574,14 @@ PeerConnectionWrapper.prototype = {
|
|||
/**
|
||||
* Sets the remote description and automatically handles the failure case.
|
||||
*
|
||||
* @param {object} sdp
|
||||
* SDP for the remote description request
|
||||
* @param {object} desc
|
||||
* mozRTCSessionDescription for the remote description request
|
||||
* @param {function} onSuccess
|
||||
* Callback to execute if the remote description was set successfully
|
||||
*/
|
||||
setRemoteDescription : function PCW_setRemoteDescription(sdp, onSuccess) {
|
||||
setRemoteDescription : function PCW_setRemoteDescription(desc, onSuccess) {
|
||||
var self = this;
|
||||
|
||||
this._pc.setRemoteDescription(sdp, function () {
|
||||
this._pc.setRemoteDescription(desc, function () {
|
||||
info("Successfully set remote description for " + self.label);
|
||||
onSuccess();
|
||||
}, unexpectedCallbackAndFinish(new Error));
|
||||
|
|
|
@ -42,11 +42,7 @@
|
|||
|
||||
makePC(1, false);
|
||||
|
||||
makePC({}, false);
|
||||
|
||||
makePC([], false);
|
||||
|
||||
makePC({ iceServers: {}}, false);
|
||||
makePC({}, true);
|
||||
|
||||
makePC({ iceServers: [] }, true);
|
||||
|
||||
|
@ -61,8 +57,6 @@
|
|||
{ url:"turns:x.org:42", username:"p", credential:"p" }
|
||||
]}, true);
|
||||
|
||||
makePC({ iceServers: [{ url:"stun:0.0.0.0", credential:{}}] }, false);
|
||||
|
||||
pcs = null;
|
||||
SimpleTest.finish();
|
||||
});
|
||||
|
|
|
@ -54,10 +54,10 @@
|
|||
[
|
||||
"SET_REMOTE_DESCRIPTION",
|
||||
function (test) {
|
||||
var osd = new mozRTCSessionDescription(offer);
|
||||
test.pcLocal.setRemoteDescription(osd, function () {
|
||||
test.next();
|
||||
});
|
||||
test.pcLocal.setRemoteDescription(new mozRTCSessionDescription(offer),
|
||||
function () {
|
||||
test.next();
|
||||
});
|
||||
}
|
||||
],
|
||||
[
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче