mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-i
This commit is contained in:
commit
e1c0a578b8
2
CLOBBER
2
CLOBBER
@ -22,4 +22,4 @@
|
|||||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||||
# don't change CLOBBER for WebIDL changes any more.
|
# don't change CLOBBER for WebIDL changes any more.
|
||||||
|
|
||||||
Bug 1182727 - Update the clang toolchain
|
No bug - unknown something produced intermittent OS X build failures in libstagefright and jemalloc and malloc
|
||||||
|
@ -586,9 +586,8 @@ setCaretOffsetCB(AtkText *aText, gint aOffset)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||||
if (proxy->SetCaretOffset(aOffset)) {
|
proxy->SetCaretOffset(aOffset);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
@ -390,13 +390,10 @@ DocAccessibleChild::RecvCaretOffset(const uint64_t& aID, int32_t* aOffset)
|
|||||||
|
|
||||||
bool
|
bool
|
||||||
DocAccessibleChild::RecvSetCaretOffset(const uint64_t& aID,
|
DocAccessibleChild::RecvSetCaretOffset(const uint64_t& aID,
|
||||||
const int32_t& aOffset,
|
const int32_t& aOffset)
|
||||||
bool* aRetVal)
|
|
||||||
{
|
{
|
||||||
HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
|
HyperTextAccessible* acc = IdToHyperTextAccessible(aID);
|
||||||
*aRetVal = false;
|
|
||||||
if (acc && acc->IsTextRole() && acc->IsValidOffset(aOffset)) {
|
if (acc && acc->IsTextRole() && acc->IsValidOffset(aOffset)) {
|
||||||
*aRetVal = true;
|
|
||||||
acc->SetCaretOffset(aOffset);
|
acc->SetCaretOffset(aOffset);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -103,8 +103,8 @@ public:
|
|||||||
override;
|
override;
|
||||||
virtual bool RecvCaretOffset(const uint64_t& aID, int32_t* aOffset)
|
virtual bool RecvCaretOffset(const uint64_t& aID, int32_t* aOffset)
|
||||||
override;
|
override;
|
||||||
virtual bool RecvSetCaretOffset(const uint64_t& aID, const int32_t& aOffset,
|
virtual bool RecvSetCaretOffset(const uint64_t& aID, const int32_t& aOffset)
|
||||||
bool* aValid) override;
|
override;
|
||||||
|
|
||||||
virtual bool RecvCharacterCount(const uint64_t& aID, int32_t* aCount)
|
virtual bool RecvCharacterCount(const uint64_t& aID, int32_t* aCount)
|
||||||
override;
|
override;
|
||||||
|
@ -92,7 +92,7 @@ child:
|
|||||||
// TextSubstring is getText in IDL.
|
// TextSubstring is getText in IDL.
|
||||||
prio(high) sync CaretLineNumber(uint64_t aID) returns(int32_t aLineNumber);
|
prio(high) sync CaretLineNumber(uint64_t aID) returns(int32_t aLineNumber);
|
||||||
prio(high) sync CaretOffset(uint64_t aID) returns(int32_t aOffset);
|
prio(high) sync CaretOffset(uint64_t aID) returns(int32_t aOffset);
|
||||||
prio(high) sync SetCaretOffset(uint64_t aID, int32_t aOffset) returns (bool aValid);
|
async SetCaretOffset(uint64_t aID, int32_t aOffset);
|
||||||
prio(high) sync CharacterCount(uint64_t aID) returns(int32_t aCount);
|
prio(high) sync CharacterCount(uint64_t aID) returns(int32_t aCount);
|
||||||
prio(high) sync SelectionCount(uint64_t aID) returns(int32_t aCount);
|
prio(high) sync SelectionCount(uint64_t aID) returns(int32_t aCount);
|
||||||
prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
|
prio(high) sync TextSubstring(uint64_t aID, int32_t aStartOffset, int32_t
|
||||||
|
@ -216,12 +216,10 @@ ProxyAccessible::CaretOffset()
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
void
|
||||||
ProxyAccessible::SetCaretOffset(int32_t aOffset)
|
ProxyAccessible::SetCaretOffset(int32_t aOffset)
|
||||||
{
|
{
|
||||||
bool valid = false;
|
unused << mDoc->SendSetCaretOffset(mID, aOffset);
|
||||||
unused << mDoc->SendSetCaretOffset(mID, aOffset, &valid);
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t
|
int32_t
|
||||||
|
@ -132,7 +132,7 @@ public:
|
|||||||
|
|
||||||
int32_t CaretLineNumber();
|
int32_t CaretLineNumber();
|
||||||
int32_t CaretOffset();
|
int32_t CaretOffset();
|
||||||
bool SetCaretOffset(int32_t aOffset);
|
void SetCaretOffset(int32_t aOffset);
|
||||||
|
|
||||||
int32_t CharacterCount();
|
int32_t CharacterCount();
|
||||||
int32_t SelectionCount();
|
int32_t SelectionCount();
|
||||||
|
@ -73,7 +73,20 @@ var OutputGenerator = {
|
|||||||
[addOutput(node) for // jshint ignore:line
|
[addOutput(node) for // jshint ignore:line
|
||||||
(node of aContext.subtreeGenerator(false, ignoreSubtree))]; // jshint ignore:line
|
(node of aContext.subtreeGenerator(false, ignoreSubtree))]; // jshint ignore:line
|
||||||
addOutput(aContext.accessible);
|
addOutput(aContext.accessible);
|
||||||
|
|
||||||
|
// If there are any documents in new ancestry, find a first one and place
|
||||||
|
// it in the beginning of the utterance.
|
||||||
|
let doc, docIndex = contextStart.findIndex(
|
||||||
|
ancestor => ancestor.role === Roles.DOCUMENT);
|
||||||
|
|
||||||
|
if (docIndex > -1) {
|
||||||
|
doc = contextStart.splice(docIndex, 1)[0];
|
||||||
|
}
|
||||||
|
|
||||||
contextStart.reverse().forEach(addOutput);
|
contextStart.reverse().forEach(addOutput);
|
||||||
|
if (doc) {
|
||||||
|
output.unshift.apply(output, self.genForObject(doc, aContext));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
|
@ -32,13 +32,13 @@
|
|||||||
// Simple traversal forward
|
// Simple traversal forward
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
['Phone status bar', 'Traversal Rule test document'],
|
['Traversal Rule test document', 'Phone status bar'],
|
||||||
{ focused: 'body' })],
|
{ focused: 'body' })],
|
||||||
[ContentMessages.simpleMovePrevious, new ExpectedNoMove()],
|
[ContentMessages.simpleMovePrevious, new ExpectedNoMove()],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[ContentMessages.simpleMoveNext, new ExpectedCursorChange(
|
[ContentMessages.simpleMoveNext, new ExpectedCursorChange(
|
||||||
['wow', {'string': 'headingLevel', 'args': [1]} ,'such app'],
|
['such app', 'wow', {'string': 'headingLevel', 'args': [1]}],
|
||||||
{ focused: 'iframe' })],
|
{ focused: 'iframe' })],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
|
new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
|
||||||
@ -79,7 +79,7 @@
|
|||||||
[ContentMessages.simpleMovePrevious,
|
[ContentMessages.simpleMovePrevious,
|
||||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||||
[ContentMessages.simpleMovePrevious,
|
[ContentMessages.simpleMovePrevious,
|
||||||
new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])],
|
new ExpectedCursorChange(['such app', 'much range', '6', {'string': 'slider'}])],
|
||||||
[ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
|
[ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
|
||||||
[ContentMessages.androidScrollForward(), new ExpectedValueChange('6')],
|
[ContentMessages.androidScrollForward(), new ExpectedValueChange('6')],
|
||||||
[ContentMessages.androidScrollBackward(), new ExpectedValueChange('5')],
|
[ContentMessages.androidScrollBackward(), new ExpectedValueChange('5')],
|
||||||
@ -106,7 +106,7 @@
|
|||||||
// fails. Bug 972035.
|
// fails. Bug 972035.
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
// Move from an inner frame to the last element in the parent doc
|
// Move from an inner frame to the last element in the parent doc
|
||||||
[ContentMessages.simpleMoveLast,
|
[ContentMessages.simpleMoveLast,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
@ -115,13 +115,13 @@
|
|||||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||||
|
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.moveOrAdjustDown('FormElement'),
|
[ContentMessages.moveOrAdjustDown('FormElement'),
|
||||||
new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
|
new ExpectedCursorChange(['Back', {"string": "pushbutton"}])],
|
||||||
[ContentMessages.moveOrAdjustDown('FormElement'),
|
[ContentMessages.moveOrAdjustDown('FormElement'),
|
||||||
new ExpectedCursorChange(['many option', {'string': 'stateNotChecked'},
|
new ExpectedCursorChange(['such app', 'many option', {'string': 'stateNotChecked'},
|
||||||
{'string': 'checkbutton'}, {'string': 'listStart'},
|
{'string': 'checkbutton'}, {'string': 'listStart'},
|
||||||
{'string': 'list'}, {'string': 'listItemsCount', 'count': 1}, 'such app'])],
|
{'string': 'list'}, {'string': 'listItemsCount', 'count': 1}])],
|
||||||
[ContentMessages.moveOrAdjustDown('FormElement'),
|
[ContentMessages.moveOrAdjustDown('FormElement'),
|
||||||
new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
|
new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
|
||||||
// Calling AdjustOrMove should adjust the range.
|
// Calling AdjustOrMove should adjust the range.
|
||||||
@ -143,11 +143,11 @@
|
|||||||
// Moving to the absolute first item from an embedded document
|
// Moving to the absolute first item from an embedded document
|
||||||
// fails. Bug 972035.
|
// fails. Bug 972035.
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
[ContentMessages.simpleMoveNext, new ExpectedCursorChange(
|
[ContentMessages.simpleMoveNext, new ExpectedCursorChange(
|
||||||
['many option', {'string': 'stateNotChecked'},
|
['many option', {'string': 'stateNotChecked'},
|
||||||
{'string': 'checkbutton'}, {'string': 'listStart'},
|
{'string': 'checkbutton'}, {'string': 'listStart'},
|
||||||
@ -160,7 +160,7 @@
|
|||||||
|
|
||||||
// Current virtual cursor's position's name changes
|
// Current virtual cursor's position's name changes
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.focusSelector('button#fruit', false),
|
[ContentMessages.focusSelector('button#fruit', false),
|
||||||
new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
|
new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
|
||||||
[doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
|
[doc.defaultView.renameFruit, new ExpectedNameChange('banana')],
|
||||||
@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
// Move cursor with focus in outside document
|
// Move cursor with focus in outside document
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.focusSelector('button#home', false),
|
[ContentMessages.focusSelector('button#home', false),
|
||||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||||
|
|
||||||
@ -188,11 +188,11 @@
|
|||||||
// Set focus on element outside of embedded frame while
|
// Set focus on element outside of embedded frame while
|
||||||
// cursor is in frame
|
// cursor is in frame
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
[ContentMessages.focusSelector('button#home', false),
|
[ContentMessages.focusSelector('button#home', false),
|
||||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||||
|
|
||||||
@ -206,12 +206,12 @@
|
|||||||
|
|
||||||
// aria-hidden element that the virtual cursor is positioned on
|
// aria-hidden element that the virtual cursor is positioned on
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[doc.defaultView.ariaHideBack,
|
[doc.defaultView.ariaHideBack,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
["wow", {"string": "headingLevel","args": [1]}, "such app"])],
|
["such app", "wow", {"string": "headingLevel","args": [1]}])],
|
||||||
// Changing aria-hidden attribute twice and making sure that the event
|
// Changing aria-hidden attribute twice and making sure that the event
|
||||||
// is fired only once when the actual change happens.
|
// is fired only once when the actual change happens.
|
||||||
[doc.defaultView.ariaHideBack],
|
[doc.defaultView.ariaHideBack],
|
||||||
@ -222,11 +222,11 @@
|
|||||||
|
|
||||||
// aria-hidden on the iframe that has the vc.
|
// aria-hidden on the iframe that has the vc.
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
new ExpectedCursorChange(['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
[doc.defaultView.ariaHideIframe,
|
[doc.defaultView.ariaHideIframe,
|
||||||
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
|
||||||
[doc.defaultView.ariaShowIframe],
|
[doc.defaultView.ariaShowIframe],
|
||||||
@ -234,37 +234,39 @@
|
|||||||
|
|
||||||
// aria-hidden element and auto Move
|
// aria-hidden element and auto Move
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[doc.defaultView.ariaHideBack],
|
[doc.defaultView.ariaHideBack],
|
||||||
[ContentMessages.focusSelector('button#back', false),
|
[ContentMessages.focusSelector('button#back', false),
|
||||||
// Must not speak Back button as it is aria-hidden
|
// Must not speak Back button as it is aria-hidden
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
["wow", {"string": "headingLevel","args": [1]}, "such app"])],
|
["such app", "wow", {"string": "headingLevel","args": [1]}])],
|
||||||
[doc.defaultView.ariaShowBack],
|
[doc.defaultView.ariaShowBack],
|
||||||
[ContentMessages.focusSelector('button#back', true), null],
|
[ContentMessages.focusSelector('button#back', true), null],
|
||||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||||
|
|
||||||
// Open dialog in outer doc, while cursor is also in outer doc
|
// Open dialog in outer doc, while cursor is also in outer doc
|
||||||
[ContentMessages.simpleMoveLast,
|
[ContentMessages.simpleMoveLast,
|
||||||
new ExpectedCursorChange(['mover'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'mover',
|
||||||
|
'medium', {'string': 'slider'}])],
|
||||||
[doc.defaultView.showAlert,
|
[doc.defaultView.showAlert,
|
||||||
new ExpectedCursorChange(['This is an alert!',
|
new ExpectedCursorChange(['This is an alert!',
|
||||||
{'string': 'headingLevel', 'args': [1]},
|
{'string': 'headingLevel', 'args': [1]},
|
||||||
{'string': 'dialog'}])],
|
{'string': 'dialog'}])],
|
||||||
|
|
||||||
[doc.defaultView.hideAlert,
|
[doc.defaultView.hideAlert,
|
||||||
new ExpectedCursorChange(['mover'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'mover',
|
||||||
|
'medium', {'string': 'slider'}])],
|
||||||
|
|
||||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||||
|
|
||||||
// Open dialog in outer doc, while cursor is in inner frame
|
// Open dialog in outer doc, while cursor is in inner frame
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
new ExpectedCursorChange(["Back", {"string": "pushbutton"}])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
[doc.defaultView.showAlert, new ExpectedCursorChange(['This is an alert!',
|
[doc.defaultView.showAlert, new ExpectedCursorChange(['This is an alert!',
|
||||||
{'string': 'headingLevel', 'args': [1]},
|
{'string': 'headingLevel', 'args': [1]},
|
||||||
{'string': 'dialog'}])],
|
{'string': 'dialog'}])],
|
||||||
@ -276,13 +278,13 @@
|
|||||||
[ContentMessages.activateCurrent(),
|
[ContentMessages.activateCurrent(),
|
||||||
new ExpectedClickAction(),
|
new ExpectedClickAction(),
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
['wow', {'string': 'headingLevel', 'args': [1]}, 'such app'])],
|
['such app', 'wow', {'string': 'headingLevel', 'args': [1]}])],
|
||||||
|
|
||||||
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
[ContentMessages.clearCursor, 'AccessFu:CursorCleared'],
|
||||||
|
|
||||||
// Open dialog, then focus on something when closing
|
// Open dialog, then focus on something when closing
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['Phone status bar', 'Traversal Rule test document'])],
|
new ExpectedCursorChange(['Traversal Rule test document', 'Phone status bar'])],
|
||||||
[doc.defaultView.showAlert,
|
[doc.defaultView.showAlert,
|
||||||
new ExpectedCursorChange(['This is an alert!',
|
new ExpectedCursorChange(['This is an alert!',
|
||||||
{'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
|
{'string': 'headingLevel', 'args': [1]}, {'string': 'dialog'}])],
|
||||||
@ -290,8 +292,8 @@
|
|||||||
[function hideAlertAndFocusHomeButton() {
|
[function hideAlertAndFocusHomeButton() {
|
||||||
doc.defaultView.hideAlert();
|
doc.defaultView.hideAlert();
|
||||||
doc.querySelector('button#home').focus();
|
doc.querySelector('button#home').focus();
|
||||||
}, new ExpectedCursorChange(['Home', {'string': 'pushbutton'},
|
}, new ExpectedCursorChange(['Traversal Rule test document',
|
||||||
'Traversal Rule test document'])],
|
'Home', {'string': 'pushbutton'}])],
|
||||||
[ContentMessages.simpleMoveNext,
|
[ContentMessages.simpleMoveNext,
|
||||||
new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])]
|
new ExpectedCursorChange(['banana', {'string': 'pushbutton'}])]
|
||||||
[ContentMessages.simpleMoveNext, new ExpectedNoMove()]
|
[ContentMessages.simpleMoveNext, new ExpectedNoMove()]
|
||||||
|
@ -31,9 +31,9 @@
|
|||||||
// Read-only text tests
|
// Read-only text tests
|
||||||
[ContentMessages.simpleMoveFirst,
|
[ContentMessages.simpleMoveFirst,
|
||||||
new ExpectedCursorChange(
|
new ExpectedCursorChange(
|
||||||
['These are my awards, Mother. From Army. The seal is for ' +
|
['Text content test document', 'These are my awards, Mother. ' +
|
||||||
'marksmanship, and the gorilla is for sand racing.',
|
'From Army. The seal is for marksmanship, and the gorilla is ' +
|
||||||
'Text content test document'])],
|
'for sand racing.'])],
|
||||||
[ContentMessages.moveNextBy('word'),
|
[ContentMessages.moveNextBy('word'),
|
||||||
new ExpectedCursorTextChange('These', 0, 5)],
|
new ExpectedCursorTextChange('These', 0, 5)],
|
||||||
[ContentMessages.moveNextBy('word'),
|
[ContentMessages.moveNextBy('word'),
|
||||||
|
@ -434,7 +434,8 @@ ia2AccessibleText::setCaretOffset(long aOffset)
|
|||||||
A11Y_TRYBLOCK_BEGIN
|
A11Y_TRYBLOCK_BEGIN
|
||||||
|
|
||||||
if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
|
if (ProxyAccessible* proxy = HyperTextProxyFor(this)) {
|
||||||
return proxy->SetCaretOffset(aOffset) ? S_OK : E_INVALIDARG;
|
proxy->SetCaretOffset(aOffset);
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
|
HyperTextAccessible* textAcc = static_cast<HyperTextAccessibleWrap*>(this);
|
||||||
|
@ -224,22 +224,27 @@ XULDropmarkerAccessible::DropmarkerOpen(bool aToggleOpen) const
|
|||||||
{
|
{
|
||||||
bool isOpen = false;
|
bool isOpen = false;
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
|
nsIContent* parent = mContent->GetFlattenedTreeParent();
|
||||||
do_QueryInterface(mContent->GetFlattenedTreeParent());
|
|
||||||
|
while (parent) {
|
||||||
|
nsCOMPtr<nsIDOMXULButtonElement> parentButtonElement =
|
||||||
|
do_QueryInterface(parent);
|
||||||
|
if (parentButtonElement) {
|
||||||
|
parentButtonElement->GetOpen(&isOpen);
|
||||||
|
if (aToggleOpen)
|
||||||
|
parentButtonElement->SetOpen(!isOpen);
|
||||||
|
return isOpen;
|
||||||
|
}
|
||||||
|
|
||||||
if (parentButtonElement) {
|
|
||||||
parentButtonElement->GetOpen(&isOpen);
|
|
||||||
if (aToggleOpen)
|
|
||||||
parentButtonElement->SetOpen(!isOpen);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
|
nsCOMPtr<nsIDOMXULMenuListElement> parentMenuListElement =
|
||||||
do_QueryInterface(parentButtonElement);
|
do_QueryInterface(parent);
|
||||||
if (parentMenuListElement) {
|
if (parentMenuListElement) {
|
||||||
parentMenuListElement->GetOpen(&isOpen);
|
parentMenuListElement->GetOpen(&isOpen);
|
||||||
if (aToggleOpen)
|
if (aToggleOpen)
|
||||||
parentMenuListElement->SetOpen(!isOpen);
|
parentMenuListElement->SetOpen(!isOpen);
|
||||||
|
return isOpen;
|
||||||
}
|
}
|
||||||
|
parent = parent->GetFlattenedTreeParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return isOpen;
|
return isOpen;
|
||||||
|
@ -698,6 +698,10 @@ var settingsToObserve = {
|
|||||||
prefName: 'dom.sms.maxReadAheadEntries',
|
prefName: 'dom.sms.maxReadAheadEntries',
|
||||||
defaultValue: 7
|
defaultValue: 7
|
||||||
},
|
},
|
||||||
|
'services.sync.enabled': {
|
||||||
|
defaultValue: false,
|
||||||
|
notifyChange: true
|
||||||
|
},
|
||||||
'ui.touch.radius.leftmm': {
|
'ui.touch.radius.leftmm': {
|
||||||
resetToPref: true
|
resetToPref: true
|
||||||
},
|
},
|
||||||
@ -717,6 +721,18 @@ var settingsToObserve = {
|
|||||||
'wap.UAProf.url': ''
|
'wap.UAProf.url': ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function settingObserver(setPref, prefName, setting) {
|
||||||
|
return value => {
|
||||||
|
setPref(prefName, value);
|
||||||
|
if (setting.notifyChange) {
|
||||||
|
SystemAppProxy._sendCustomEvent('mozPrefChromeEvent', {
|
||||||
|
prefName: prefName,
|
||||||
|
value: value
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
for (let key in settingsToObserve) {
|
for (let key in settingsToObserve) {
|
||||||
let setting = settingsToObserve[key];
|
let setting = settingsToObserve[key];
|
||||||
|
|
||||||
@ -766,7 +782,6 @@ for (let key in settingsToObserve) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SettingsListener.observe(key, defaultValue, function(value) {
|
SettingsListener.observe(key, defaultValue,
|
||||||
setPref(prefName, value);
|
settingObserver(setPref, prefName, setting));
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
@ -156,7 +156,8 @@ this.FxAccountsMgmtService = {
|
|||||||
case "signIn":
|
case "signIn":
|
||||||
case "signUp":
|
case "signUp":
|
||||||
case "refreshAuthentication":
|
case "refreshAuthentication":
|
||||||
FxAccountsManager[data.method](data.email, data.password).then(
|
FxAccountsManager[data.method](data.email, data.password,
|
||||||
|
data.fetchKeys).then(
|
||||||
user => {
|
user => {
|
||||||
self._onFulfill(msg.id, user);
|
self._onFulfill(msg.id, user);
|
||||||
},
|
},
|
||||||
|
@ -602,10 +602,7 @@ menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
|
|||||||
menuitem.spell-suggestion {
|
menuitem.spell-suggestion {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
/* apply Fitts' law to the notification bar's close button */
|
|
||||||
window:not([sizemode="normal"]) .notification-inner {
|
|
||||||
-moz-border-end-width: 0 !important;
|
|
||||||
}
|
|
||||||
/* Hide extension toolbars that neglected to set the proper class */
|
/* Hide extension toolbars that neglected to set the proper class */
|
||||||
window[chromehidden~="location"][chromehidden~="toolbar"] toolbar:not(.chromeclass-menubar),
|
window[chromehidden~="location"][chromehidden~="toolbar"] toolbar:not(.chromeclass-menubar),
|
||||||
window[chromehidden~="toolbar"] toolbar:not(#nav-bar):not(#TabsToolbar):not(#print-preview-toolbar):not(.chromeclass-menubar) {
|
window[chromehidden~="toolbar"] toolbar:not(#nav-bar):not(#TabsToolbar):not(#print-preview-toolbar):not(.chromeclass-menubar) {
|
||||||
|
@ -2345,6 +2345,15 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
|
|||||||
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
|
let browserWindow = RecentWindow.getMostRecentBrowserWindow();
|
||||||
tabBrowser = browserWindow.gBrowser;
|
tabBrowser = browserWindow.gBrowser;
|
||||||
}
|
}
|
||||||
|
// Some internal URLs (such as specific chrome: and about: URLs that are
|
||||||
|
// not yet remote ready) cannot be loaded in a remote browser. View
|
||||||
|
// source in tab expects the new view source browser's remoteness to match
|
||||||
|
// that of the original URL, so disable remoteness if necessary for this
|
||||||
|
// URL.
|
||||||
|
let contentProcess = Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT
|
||||||
|
let forceNotRemote =
|
||||||
|
gMultiProcessBrowser &&
|
||||||
|
!E10SUtils.canLoadURIInProcess(args.URL, contentProcess);
|
||||||
// `viewSourceInBrowser` will load the source content from the page
|
// `viewSourceInBrowser` will load the source content from the page
|
||||||
// descriptor for the tab (when possible) or fallback to the network if
|
// descriptor for the tab (when possible) or fallback to the network if
|
||||||
// that fails. Either way, the view source module will manage the tab's
|
// that fails. Either way, the view source module will manage the tab's
|
||||||
@ -2352,7 +2361,8 @@ function BrowserViewSourceOfDocument(aArgsOrDocument) {
|
|||||||
// requests.
|
// requests.
|
||||||
let tab = tabBrowser.loadOneTab("about:blank", {
|
let tab = tabBrowser.loadOneTab("about:blank", {
|
||||||
relatedToCurrent: true,
|
relatedToCurrent: true,
|
||||||
inBackground: false
|
inBackground: false,
|
||||||
|
forceNotRemote,
|
||||||
});
|
});
|
||||||
args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
|
args.viewSourceBrowser = tabBrowser.getBrowserForTab(tab);
|
||||||
top.gViewSourceUtils.viewSourceInBrowser(args);
|
top.gViewSourceUtils.viewSourceInBrowser(args);
|
||||||
|
@ -711,27 +711,50 @@
|
|||||||
onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
|
onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
|
||||||
onblur="setTimeout(() => { document.getElementById('identity-box').style.MozUserFocus = ''; }, 0);">
|
onblur="setTimeout(() => { document.getElementById('identity-box').style.MozUserFocus = ''; }, 0);">
|
||||||
<box id="notification-popup-box" hidden="true" align="center">
|
<box id="notification-popup-box" hidden="true" align="center">
|
||||||
<image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="default-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.defaultNotificationAnchor.label;"/>
|
||||||
|
<!-- NB: the identity-notification-icon is used for persona-based auth and preffed
|
||||||
|
off by default. It hasn't been updated for some time and liable to being
|
||||||
|
removed.-->
|
||||||
<image id="identity-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="identity-notification-icon" class="notification-anchor-icon" role="button"/>
|
||||||
<image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="geo-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.geolocationNotificationAnchor.label;"/>
|
||||||
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="login-fill-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.addonsNotificationAnchor.label;"/>
|
||||||
<image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.indexedDBNotificationAnchor.label;"/>
|
||||||
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="login-fill-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.loginFillNotificationAnchor.label;"/>
|
||||||
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="password-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.passwordNotificationAnchor.label;"/>
|
||||||
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="webapps-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.webappsNotificationAnchor.label;"/>
|
||||||
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="plugins-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.pluginsNotificationAnchor.label;"/>
|
||||||
<image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="web-notifications-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.webNotsNotificationAnchor.label;"/>
|
||||||
<image id="translate-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="webRTC-shareDevices-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
<image id="translated-notification-icon" class="notification-anchor-icon" role="button"/>
|
aria-label="&urlbar.webRTCShareDevicesNotificationAnchor.label;"/>
|
||||||
<image id="eme-notification-icon" class="notification-anchor-icon" role="button"/>
|
<image id="webRTC-sharingDevices-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.webRTCSharingDevicesNotificationAnchor.label;"/>
|
||||||
|
<image id="webRTC-shareMicrophone-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.webRTCShareMicrophoneNotificationAnchor.label;"/>
|
||||||
|
<image id="webRTC-sharingMicrophone-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.webRTCSharingMicrophoneNotificationAnchor.label;"/>
|
||||||
|
<image id="webRTC-shareScreen-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.webRTCShareScreenNotificationAnchor.label;"/>
|
||||||
|
<image id="webRTC-sharingScreen-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.webRTCSharingScreenNotificationAnchor.label;"/>
|
||||||
|
<image id="pointerLock-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.pointerLockNotificationAnchor.label;"/>
|
||||||
|
<image id="servicesInstall-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.servicesNotificationAnchor.label;"/>
|
||||||
|
<image id="translate-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.translateNotificationAnchor.label;"/>
|
||||||
|
<image id="translated-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.translatedNotificationAnchor.label;"/>
|
||||||
|
<image id="eme-notification-icon" class="notification-anchor-icon" role="button"
|
||||||
|
aria-label="&urlbar.emeNotificationAnchor.label;"/>
|
||||||
</box>
|
</box>
|
||||||
<!-- Use onclick instead of normal popup= syntax since the popup
|
<!-- Use onclick instead of normal popup= syntax since the popup
|
||||||
code fires onmousedown, and hence eats our favicon drag events.
|
code fires onmousedown, and hence eats our favicon drag events.
|
||||||
@ -739,6 +762,7 @@
|
|||||||
has focus, otherwise pressing F6 focuses it instead of the location bar -->
|
has focus, otherwise pressing F6 focuses it instead of the location bar -->
|
||||||
<box id="identity-box" role="button"
|
<box id="identity-box" role="button"
|
||||||
align="center"
|
align="center"
|
||||||
|
aria-label="&urlbar.viewSiteInfo.label;"
|
||||||
onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
|
onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
|
||||||
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
|
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
|
||||||
ondragstart="gIdentityHandler.onDragStart(event);">
|
ondragstart="gIdentityHandler.onDragStart(event);">
|
||||||
@ -775,13 +799,16 @@
|
|||||||
<toolbarbutton id="urlbar-go-button"
|
<toolbarbutton id="urlbar-go-button"
|
||||||
class="chromeclass-toolbar-additional"
|
class="chromeclass-toolbar-additional"
|
||||||
onclick="gURLBar.handleCommand(event);"
|
onclick="gURLBar.handleCommand(event);"
|
||||||
|
aria-label="&goEndCap.tooltip;"
|
||||||
tooltiptext="&goEndCap.tooltip;"/>
|
tooltiptext="&goEndCap.tooltip;"/>
|
||||||
<toolbarbutton id="urlbar-reload-button"
|
<toolbarbutton id="urlbar-reload-button"
|
||||||
class="chromeclass-toolbar-additional"
|
class="chromeclass-toolbar-additional"
|
||||||
command="Browser:ReloadOrDuplicate"
|
command="Browser:ReloadOrDuplicate"
|
||||||
onclick="checkForMiddleClick(this, event);"
|
onclick="checkForMiddleClick(this, event);"
|
||||||
|
aria-label="&reloadButton.tooltip;"
|
||||||
tooltiptext="&reloadButton.tooltip;"/>
|
tooltiptext="&reloadButton.tooltip;"/>
|
||||||
<toolbarbutton id="urlbar-stop-button"
|
<toolbarbutton id="urlbar-stop-button"
|
||||||
|
aria-label="&stopButton.tooltip;"
|
||||||
class="chromeclass-toolbar-additional"
|
class="chromeclass-toolbar-additional"
|
||||||
command="Browser:Stop"
|
command="Browser:Stop"
|
||||||
tooltiptext="&stopButton.tooltip;"/>
|
tooltiptext="&stopButton.tooltip;"/>
|
||||||
@ -1092,7 +1119,7 @@
|
|||||||
|
|
||||||
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
|
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
|
||||||
<vbox id="appcontent" flex="1">
|
<vbox id="appcontent" flex="1">
|
||||||
<notificationbox id="high-priority-global-notificationbox"/>
|
<notificationbox id="high-priority-global-notificationbox" notificationside="top"/>
|
||||||
<tabbrowser id="content"
|
<tabbrowser id="content"
|
||||||
flex="1" contenttooltip="aHTMLTooltip"
|
flex="1" contenttooltip="aHTMLTooltip"
|
||||||
tabcontainer="tabbrowser-tabs"
|
tabcontainer="tabbrowser-tabs"
|
||||||
@ -1175,7 +1202,7 @@
|
|||||||
</html:div>
|
</html:div>
|
||||||
|
|
||||||
<vbox id="browser-bottombox" layer="true">
|
<vbox id="browser-bottombox" layer="true">
|
||||||
<notificationbox id="global-notificationbox"/>
|
<notificationbox id="global-notificationbox" notificationside="bottom"/>
|
||||||
<toolbar id="developer-toolbar"
|
<toolbar id="developer-toolbar"
|
||||||
hidden="true">
|
hidden="true">
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
|
flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
|
||||||
onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
|
onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
|
||||||
<xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
|
<xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
|
||||||
<xul:notificationbox flex="1">
|
<xul:notificationbox flex="1" notificationside="top">
|
||||||
<xul:hbox flex="1" class="browserSidebarContainer">
|
<xul:hbox flex="1" class="browserSidebarContainer">
|
||||||
<xul:vbox flex="1" class="browserContainer">
|
<xul:vbox flex="1" class="browserContainer">
|
||||||
<xul:stack flex="1" class="browserStack" anonid="browserStack">
|
<xul:stack flex="1" class="browserStack" anonid="browserStack">
|
||||||
@ -1743,6 +1743,7 @@
|
|||||||
var notificationbox = document.createElementNS(NS_XUL,
|
var notificationbox = document.createElementNS(NS_XUL,
|
||||||
"notificationbox");
|
"notificationbox");
|
||||||
notificationbox.setAttribute("flex", "1");
|
notificationbox.setAttribute("flex", "1");
|
||||||
|
notificationbox.setAttribute("notificationside", "top");
|
||||||
notificationbox.appendChild(browserSidebarContainer);
|
notificationbox.appendChild(browserSidebarContainer);
|
||||||
|
|
||||||
// Prevent the superfluous initial load of a blank document
|
// Prevent the superfluous initial load of a blank document
|
||||||
|
@ -41,6 +41,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||||||
</xul:hbox>
|
</xul:hbox>
|
||||||
<xul:dropmarker anonid="historydropmarker"
|
<xul:dropmarker anonid="historydropmarker"
|
||||||
class="autocomplete-history-dropmarker urlbar-history-dropmarker"
|
class="autocomplete-history-dropmarker urlbar-history-dropmarker"
|
||||||
|
aria-label="&urlbar.toggleAutocomplete.label;"
|
||||||
allowevents="true"
|
allowevents="true"
|
||||||
xbl:inherits="open,enablehistory,parentfocused=focused"/>
|
xbl:inherits="open,enablehistory,parentfocused=focused"/>
|
||||||
<children includes="hbox"/>
|
<children includes="hbox"/>
|
||||||
|
@ -4,8 +4,9 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
pref("startup.homepage_override_url","");
|
pref("startup.homepage_override_url", "");
|
||||||
pref("startup.homepage_welcome_url","https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
|
pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
|
||||||
|
pref("startup.homepage_welcome_url.additional", "");
|
||||||
// The time interval between checks for a new version (in seconds)
|
// The time interval between checks for a new version (in seconds)
|
||||||
pref("app.update.interval", 28800); // 8 hours
|
pref("app.update.interval", 28800); // 8 hours
|
||||||
// The time interval between the downloading of mar file chunks in the
|
// The time interval between the downloading of mar file chunks in the
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
pref("startup.homepage_override_url", "https://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
|
pref("startup.homepage_override_url", "https://www.mozilla.org/projects/firefox/%VERSION%/whatsnew/?oldversion=%OLD_VERSION%");
|
||||||
pref("startup.homepage_welcome_url", "https://www.mozilla.org/projects/firefox/%VERSION%/firstrun/");
|
pref("startup.homepage_welcome_url", "https://www.mozilla.org/projects/firefox/%VERSION%/firstrun/");
|
||||||
|
pref("startup.homepage_welcome_url.additional", "");
|
||||||
// The time interval between checks for a new version (in seconds)
|
// The time interval between checks for a new version (in seconds)
|
||||||
pref("app.update.interval", 7200); // 2 hours
|
pref("app.update.interval", 7200); // 2 hours
|
||||||
// The time interval between the downloading of mar file chunks in the
|
// The time interval between the downloading of mar file chunks in the
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
pref("startup.homepage_override_url","");
|
pref("startup.homepage_override_url", "");
|
||||||
pref("startup.homepage_welcome_url","https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
|
pref("startup.homepage_welcome_url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/");
|
||||||
|
pref("startup.homepage_welcome_url.additional", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/firstrun/learnmore/");
|
||||||
// Interval: Time between checks for a new version (in seconds)
|
// Interval: Time between checks for a new version (in seconds)
|
||||||
pref("app.update.interval", 43200); // 12 hours
|
pref("app.update.interval", 43200); // 12 hours
|
||||||
// The time interval between the downloading of mar file chunks in the
|
// The time interval between the downloading of mar file chunks in the
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
pref("startup.homepage_override_url","");
|
pref("startup.homepage_override_url", "");
|
||||||
pref("startup.homepage_welcome_url","");
|
pref("startup.homepage_welcome_url", "");
|
||||||
|
pref("startup.homepage_welcome_url.additional", "");
|
||||||
// The time interval between checks for a new version (in seconds)
|
// The time interval between checks for a new version (in seconds)
|
||||||
pref("app.update.interval", 86400); // 24 hours
|
pref("app.update.interval", 86400); // 24 hours
|
||||||
// The time interval between the downloading of mar file chunks in the
|
// The time interval between the downloading of mar file chunks in the
|
||||||
|
@ -55,8 +55,7 @@ loop.shared.actions = (function() {
|
|||||||
|
|
||||||
// Optional Items. There are other optional items typically sent
|
// Optional Items. There are other optional items typically sent
|
||||||
// around with this action. They are for the setup of calls and rooms and
|
// around with this action. They are for the setup of calls and rooms and
|
||||||
// depend on the type. See LoopCalls and LoopRooms for the details of this
|
// depend on the type. See LoopRooms for the details of this data.
|
||||||
// data.
|
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,56 +74,6 @@ loop.shared.actions = (function() {
|
|||||||
WindowUnload: Action.define("windowUnload", {
|
WindowUnload: Action.define("windowUnload", {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetch a new room url from the server, intended to be sent over email when
|
|
||||||
* a contact can't be reached.
|
|
||||||
*/
|
|
||||||
FetchRoomEmailLink: Action.define("fetchRoomEmailLink", {
|
|
||||||
roomName: String
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to cancel call setup.
|
|
||||||
*/
|
|
||||||
CancelCall: Action.define("cancelCall", {
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to retry a failed call.
|
|
||||||
*/
|
|
||||||
RetryCall: Action.define("retryCall", {
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals when the user wishes to accept a call.
|
|
||||||
*/
|
|
||||||
AcceptCall: Action.define("acceptCall", {
|
|
||||||
callType: String
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals when the user declines a call.
|
|
||||||
*/
|
|
||||||
DeclineCall: Action.define("declineCall", {
|
|
||||||
blockCaller: Boolean
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to initiate connecting of a call with the relevant
|
|
||||||
* sessionData.
|
|
||||||
*/
|
|
||||||
ConnectCall: Action.define("connectCall", {
|
|
||||||
// This object contains the necessary details for the
|
|
||||||
// connection of the websocket, and the SDK
|
|
||||||
sessionData: Object
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for hanging up the call at the end of a successful call.
|
|
||||||
*/
|
|
||||||
HangupCall: Action.define("hangupCall", {
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to indicate the remote peer was disconnected for some reason.
|
* Used to indicate the remote peer was disconnected for some reason.
|
||||||
*
|
*
|
||||||
@ -134,16 +83,6 @@ loop.shared.actions = (function() {
|
|||||||
peerHungup: Boolean
|
peerHungup: Boolean
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for notifying of connection progress state changes.
|
|
||||||
* The connection refers to the overall connection flow as indicated
|
|
||||||
* on the websocket.
|
|
||||||
*/
|
|
||||||
ConnectionProgress: Action.define("connectionProgress", {
|
|
||||||
// The connection state from the websocket.
|
|
||||||
wsState: String
|
|
||||||
}),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for notifying of connection failures.
|
* Used for notifying of connection failures.
|
||||||
*/
|
*/
|
||||||
|
@ -1,463 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["LoopCalls"];
|
|
||||||
|
|
||||||
const EMAIL_OR_PHONE_RE = /^(:?\S+@\S+|\+\d+)$/;
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopService",
|
|
||||||
"resource:///modules/loop/MozLoopService.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LOOP_SESSION_TYPE",
|
|
||||||
"resource:///modules/loop/MozLoopService.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
|
|
||||||
"resource:///modules/loop/LoopContacts.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
|
||||||
"resource://gre/modules/Task.jsm");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to open a websocket.
|
|
||||||
*
|
|
||||||
* A new websocket interface is used each time. If an onStop callback
|
|
||||||
* was received, calling asyncOpen() on the same interface will
|
|
||||||
* trigger a "alreay open socket" exception even though the channel
|
|
||||||
* is logically closed.
|
|
||||||
*/
|
|
||||||
function CallProgressSocket(progressUrl, callId, token) {
|
|
||||||
if (!progressUrl || !callId || !token) {
|
|
||||||
throw new Error("missing required arguments");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._progressUrl = progressUrl;
|
|
||||||
this._callId = callId;
|
|
||||||
this._token = token;
|
|
||||||
}
|
|
||||||
|
|
||||||
CallProgressSocket.prototype = {
|
|
||||||
/**
|
|
||||||
* Open websocket and run hello exchange.
|
|
||||||
* Sends a hello message to the server.
|
|
||||||
*
|
|
||||||
* @param {function} Callback used after a successful handshake
|
|
||||||
* over the progressUrl.
|
|
||||||
* @param {function} Callback used if an error is encountered
|
|
||||||
*/
|
|
||||||
connect: function(onSuccess, onError) {
|
|
||||||
this._onSuccess = onSuccess;
|
|
||||||
this._onError = onError || (reason => {
|
|
||||||
MozLoopService.log.warn("LoopCalls::callProgessSocket - ", reason);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!onSuccess) {
|
|
||||||
this._onError("missing onSuccess argument");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Services.io.offline) {
|
|
||||||
this._onError("IO offline");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uri = Services.io.newURI(this._progressUrl, null, null);
|
|
||||||
|
|
||||||
// Allow _websocket to be set for testing.
|
|
||||||
if (!this._websocket) {
|
|
||||||
this._websocket = Cc["@mozilla.org/network/protocol;1?name=" + uri.scheme]
|
|
||||||
.createInstance(Ci.nsIWebSocketChannel);
|
|
||||||
|
|
||||||
this._websocket.initLoadInfo(null, // aLoadingNode
|
|
||||||
Services.scriptSecurityManager.getSystemPrincipal(),
|
|
||||||
null, // aTriggeringPrincipal
|
|
||||||
Ci.nsILoadInfo.SEC_NORMAL,
|
|
||||||
Ci.nsIContentPolicy.TYPE_WEBSOCKET);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._websocket.asyncOpen(uri, this._progressUrl, this, null);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener method, handles the start of the websocket stream.
|
|
||||||
* Sends a hello message to the server.
|
|
||||||
*
|
|
||||||
* @param {nsISupports} aContext Not used
|
|
||||||
*/
|
|
||||||
onStart: function() {
|
|
||||||
let helloMsg = {
|
|
||||||
messageType: "hello",
|
|
||||||
callId: this._callId,
|
|
||||||
auth: this._token
|
|
||||||
};
|
|
||||||
try { // in case websocket has closed before this handler is run
|
|
||||||
this._websocket.sendMsg(JSON.stringify(helloMsg));
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
this._onError(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener method, called when the websocket is closed.
|
|
||||||
*
|
|
||||||
* @param {nsISupports} aContext Not used
|
|
||||||
* @param {nsresult} aStatusCode Reason for stopping (NS_OK = successful)
|
|
||||||
*/
|
|
||||||
onStop: function(aContext, aStatusCode) {
|
|
||||||
if (!this._handshakeComplete) {
|
|
||||||
this._onError("[" + aStatusCode + "]");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener method, called when the websocket is closed by the server.
|
|
||||||
* If there are errors, onStop may be called without ever calling this
|
|
||||||
* method.
|
|
||||||
*
|
|
||||||
* @param {nsISupports} aContext Not used
|
|
||||||
* @param {integer} aCode the websocket closing handshake close code
|
|
||||||
* @param {String} aReason the websocket closing handshake close reason
|
|
||||||
*/
|
|
||||||
onServerClose: function(aContext, aCode, aReason) {
|
|
||||||
if (!this._handshakeComplete) {
|
|
||||||
this._onError("[" + aCode + "]" + aReason);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener method, called when the websocket receives a message.
|
|
||||||
*
|
|
||||||
* @param {nsISupports} aContext Not used
|
|
||||||
* @param {String} aMsg The message data
|
|
||||||
*/
|
|
||||||
onMessageAvailable: function(aContext, aMsg) {
|
|
||||||
let msg = {};
|
|
||||||
try {
|
|
||||||
msg = JSON.parse(aMsg);
|
|
||||||
} catch (error) {
|
|
||||||
MozLoopService.log.error("LoopCalls: error parsing progress message - ", error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.messageType && msg.messageType === "hello") {
|
|
||||||
this._handshakeComplete = true;
|
|
||||||
this._onSuccess();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a JSON message payload and send on websocket.
|
|
||||||
*
|
|
||||||
* @param {Object} aMsg Message to send.
|
|
||||||
*/
|
|
||||||
_send: function(aMsg) {
|
|
||||||
if (!this._handshakeComplete) {
|
|
||||||
MozLoopService.log.warn("LoopCalls::_send error - handshake not complete");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._websocket.sendMsg(JSON.stringify(aMsg));
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
this._onError(error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the server that the user has declined the call
|
|
||||||
* with a reason of busy.
|
|
||||||
*/
|
|
||||||
sendBusy: function() {
|
|
||||||
this._send({
|
|
||||||
messageType: "action",
|
|
||||||
event: "terminate",
|
|
||||||
reason: "busy"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal helper methods and state
|
|
||||||
*
|
|
||||||
* The registration is a two-part process. First we need to connect to
|
|
||||||
* and register with the push server. Then we need to take the result of that
|
|
||||||
* and register with the Loop server.
|
|
||||||
*/
|
|
||||||
var LoopCallsInternal = {
|
|
||||||
mocks: {
|
|
||||||
webSocket: undefined
|
|
||||||
},
|
|
||||||
|
|
||||||
conversationInProgress: {},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback from MozLoopPushHandler - A push notification has been received from
|
|
||||||
* the server.
|
|
||||||
*
|
|
||||||
* @param {String} version The version information from the server.
|
|
||||||
*/
|
|
||||||
onNotification: function(version, channelID) {
|
|
||||||
if (MozLoopService.doNotDisturb) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request the information on the new call(s) associated with this version.
|
|
||||||
// The registered FxA session is checked first, then the anonymous session.
|
|
||||||
// Make the call to get the GUEST session regardless of whether the FXA
|
|
||||||
// request fails.
|
|
||||||
|
|
||||||
if (channelID == MozLoopService.channelIDs.callsFxA && MozLoopService.userProfile) {
|
|
||||||
this._getCalls(LOOP_SESSION_TYPE.FXA, version);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make a hawkRequest to GET/calls?=version for this session type.
|
|
||||||
*
|
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType - type of hawk token used
|
|
||||||
* for the GET operation.
|
|
||||||
* @param {Object} version - LoopPushService notification version
|
|
||||||
*
|
|
||||||
* @returns {Promise}
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
_getCalls: function(sessionType, version) {
|
|
||||||
return MozLoopService.hawkRequest(sessionType, "/calls?version=" + version, "GET").then(
|
|
||||||
response => { this._processCalls(response, sessionType); }
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the calls array returned from a GET/calls?version request.
|
|
||||||
* Only one active call is permitted at this time.
|
|
||||||
*
|
|
||||||
* @param {Object} response - response payload from GET
|
|
||||||
*
|
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType - type of hawk token used
|
|
||||||
* for the GET operation.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
_processCalls: function(response, sessionType) {
|
|
||||||
try {
|
|
||||||
let respData = JSON.parse(response.body);
|
|
||||||
if (respData.calls && Array.isArray(respData.calls)) {
|
|
||||||
respData.calls.forEach((callData) => {
|
|
||||||
if ("id" in this.conversationInProgress) {
|
|
||||||
this._returnBusy(callData);
|
|
||||||
} else {
|
|
||||||
callData.sessionType = sessionType;
|
|
||||||
callData.type = "incoming";
|
|
||||||
this._startCall(callData);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
MozLoopService.log.warn("Error: missing calls[] in response");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
MozLoopService.log.warn("Error parsing calls info", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a call, saves the call data, and opens a chat window.
|
|
||||||
*
|
|
||||||
* @param {Object} callData The data associated with the call including an id.
|
|
||||||
* The data should include the type - "incoming" or
|
|
||||||
* "outgoing".
|
|
||||||
*/
|
|
||||||
_startCall: function(callData) {
|
|
||||||
const openChat = () => {
|
|
||||||
let windowId = MozLoopService.openChatWindow(callData);
|
|
||||||
if (windowId) {
|
|
||||||
this.conversationInProgress.id = windowId;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (callData.type == "incoming" && ("callerId" in callData) &&
|
|
||||||
EMAIL_OR_PHONE_RE.test(callData.callerId)) {
|
|
||||||
LoopContacts.search({
|
|
||||||
q: callData.callerId,
|
|
||||||
field: callData.callerId.includes("@") ? "email" : "tel"
|
|
||||||
}, (err, contacts) => {
|
|
||||||
if (err) {
|
|
||||||
// Database error, helas!
|
|
||||||
openChat();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let contact of contacts) {
|
|
||||||
if (contact.blocked) {
|
|
||||||
// Blocked! Send a busy signal back to the caller.
|
|
||||||
this._returnBusy(callData);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openChat();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
openChat();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a direct call to the contact addresses.
|
|
||||||
*
|
|
||||||
* @param {Object} contact The contact to call
|
|
||||||
* @param {String} callType The type of call, e.g. "audio-video" or "audio-only"
|
|
||||||
* @return true if the call is opened, false if it is not opened (i.e. busy)
|
|
||||||
*/
|
|
||||||
startDirectCall: function(contact, callType) {
|
|
||||||
if ("id" in this.conversationInProgress) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var callData = {
|
|
||||||
contact: contact,
|
|
||||||
callType: callType,
|
|
||||||
type: "outgoing"
|
|
||||||
};
|
|
||||||
|
|
||||||
this._startCall(callData);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Block a caller so it will show up in the contacts list as a blocked contact.
|
|
||||||
* If the contact is not yet part of the users' contacts list, it will be added
|
|
||||||
* as a blocked contact directly.
|
|
||||||
*
|
|
||||||
* @param {String} callerId Email address or phone number that may identify
|
|
||||||
* the caller as an existing contact
|
|
||||||
* @param {Function} callback Function that will be invoked once the operation
|
|
||||||
* has completed. When an error occurs, it will be
|
|
||||||
* passed as its first argument
|
|
||||||
*/
|
|
||||||
blockDirectCaller: function(callerId, callback) {
|
|
||||||
let field = callerId.contains("@") ? "email" : "tel";
|
|
||||||
Task.spawn(function* () {
|
|
||||||
// See if we can find the caller in our database.
|
|
||||||
let contacts = yield LoopContacts.promise("search", {
|
|
||||||
q: callerId,
|
|
||||||
field: field
|
|
||||||
});
|
|
||||||
|
|
||||||
let contact;
|
|
||||||
if (contacts.length) {
|
|
||||||
for (contact of contacts) {
|
|
||||||
yield LoopContacts.promise("block", contact._guid);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// If the contact doesn't exist yet, add it as a blocked contact.
|
|
||||||
contact = {
|
|
||||||
id: MozLoopService.generateUUID(),
|
|
||||||
name: [callerId],
|
|
||||||
category: ["local"],
|
|
||||||
blocked: true
|
|
||||||
};
|
|
||||||
// Add the phone OR email field to the contact.
|
|
||||||
contact[field] = [{
|
|
||||||
pref: true,
|
|
||||||
value: callerId
|
|
||||||
}];
|
|
||||||
|
|
||||||
yield LoopContacts.promise("add", contact);
|
|
||||||
}
|
|
||||||
}).then(callback, callback);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open call progress websocket and terminate with a reason of busy
|
|
||||||
* the server.
|
|
||||||
*
|
|
||||||
* @param {callData} Must contain the progressURL, callId and websocketToken
|
|
||||||
* returned by the LoopService.
|
|
||||||
*/
|
|
||||||
_returnBusy: function(callData) {
|
|
||||||
let callProgress = new CallProgressSocket(
|
|
||||||
callData.progressURL,
|
|
||||||
callData.callId,
|
|
||||||
callData.websocketToken);
|
|
||||||
if (this.mocks.webSocket) {
|
|
||||||
callProgress._websocket = this.mocks.webSocket;
|
|
||||||
}
|
|
||||||
// This instance of CallProgressSocket should stay alive until the underlying
|
|
||||||
// websocket is closed since it is passed to the websocket as the nsIWebSocketListener.
|
|
||||||
callProgress.connect(() => { callProgress.sendBusy(); });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Object.freeze(LoopCallsInternal);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Public API
|
|
||||||
*/
|
|
||||||
this.LoopCalls = {
|
|
||||||
/**
|
|
||||||
* Callback from MozLoopPushHandler - A push notification has been received from
|
|
||||||
* the server.
|
|
||||||
*
|
|
||||||
* @param {String} version The version information from the server.
|
|
||||||
*/
|
|
||||||
onNotification: function(version, channelID) {
|
|
||||||
LoopCallsInternal.onNotification(version, channelID);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to signify that a call is in progress.
|
|
||||||
*
|
|
||||||
* @param {String} The window id for the call in progress.
|
|
||||||
*/
|
|
||||||
setCallInProgress: function(conversationWindowId) {
|
|
||||||
if ("id" in LoopCallsInternal.conversationInProgress &&
|
|
||||||
LoopCallsInternal.conversationInProgress.id != conversationWindowId) {
|
|
||||||
MozLoopService.log.error("Starting a new conversation when one is already in progress?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoopCallsInternal.conversationInProgress.id = conversationWindowId;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the callData for a specific conversation window id.
|
|
||||||
*
|
|
||||||
* The result of this call will be a free call session slot.
|
|
||||||
*
|
|
||||||
* @param {Number} conversationWindowId
|
|
||||||
*/
|
|
||||||
clearCallInProgress: function(conversationWindowId) {
|
|
||||||
if ("id" in LoopCallsInternal.conversationInProgress &&
|
|
||||||
LoopCallsInternal.conversationInProgress.id == conversationWindowId) {
|
|
||||||
delete LoopCallsInternal.conversationInProgress.id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts a direct call to the contact addresses.
|
|
||||||
*
|
|
||||||
* @param {Object} contact The contact to call
|
|
||||||
* @param {String} callType The type of call, e.g. "audio-video" or "audio-only"
|
|
||||||
* @return true if the call is opened, false if it is not opened (i.e. busy)
|
|
||||||
*/
|
|
||||||
startDirectCall: function(contact, callType) {
|
|
||||||
LoopCallsInternal.startDirectCall(contact, callType);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @see LoopCallsInternal#blockDirectCaller
|
|
||||||
*/
|
|
||||||
blockDirectCaller: function(callerId, callback) {
|
|
||||||
return LoopCallsInternal.blockDirectCaller(callerId, callback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Object.freeze(LoopCalls);
|
|
@ -9,7 +9,6 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
|||||||
Cu.import("resource://services-common/utils.js");
|
Cu.import("resource://services-common/utils.js");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
|
||||||
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopContacts.jsm");
|
Cu.import("resource:///modules/loop/LoopContacts.jsm");
|
||||||
@ -372,8 +371,8 @@ function injectLoopAPI(targetWindow) {
|
|||||||
/**
|
/**
|
||||||
* Returns the window data for a specific conversation window id.
|
* Returns the window data for a specific conversation window id.
|
||||||
*
|
*
|
||||||
* This data will be relevant to the type of window, e.g. rooms or calls.
|
* This data will be relevant to the type of window, e.g. rooms.
|
||||||
* See LoopRooms or LoopCalls for more information.
|
* See LoopRooms for more information.
|
||||||
*
|
*
|
||||||
* @param {String} conversationWindowId
|
* @param {String} conversationWindowId
|
||||||
* @returns {Object} The window data or null if error.
|
* @returns {Object} The window data or null if error.
|
||||||
@ -423,22 +422,6 @@ function injectLoopAPI(targetWindow) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the calls API.
|
|
||||||
*
|
|
||||||
* @returns {Object} The rooms API object
|
|
||||||
*/
|
|
||||||
calls: {
|
|
||||||
enumerable: true,
|
|
||||||
get: function() {
|
|
||||||
if (callsAPI) {
|
|
||||||
return callsAPI;
|
|
||||||
}
|
|
||||||
|
|
||||||
return callsAPI = injectObjectAPI(LoopCalls, targetWindow);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Import a list of (new) contacts from an external data source.
|
* Import a list of (new) contacts from an external data source.
|
||||||
*
|
*
|
||||||
@ -609,50 +592,6 @@ function injectLoopAPI(targetWindow) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs a hawk based request to the loop server.
|
|
||||||
*
|
|
||||||
* Callback parameters:
|
|
||||||
* - {Object|null} null if success. Otherwise an object:
|
|
||||||
* {
|
|
||||||
* code: 401,
|
|
||||||
* errno: 401,
|
|
||||||
* error: "Request failed",
|
|
||||||
* message: "invalid token"
|
|
||||||
* }
|
|
||||||
* - {String} The body of the response.
|
|
||||||
*
|
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session to use for
|
|
||||||
* the request. This is one of the
|
|
||||||
* LOOP_SESSION_TYPE members
|
|
||||||
* @param {String} path The path to make the request to.
|
|
||||||
* @param {String} method The request method, e.g. 'POST', 'GET'.
|
|
||||||
* @param {Object} payloadObj An object which is converted to JSON and
|
|
||||||
* transmitted with the request.
|
|
||||||
* @param {Function} callback Called when the request completes.
|
|
||||||
*/
|
|
||||||
hawkRequest: {
|
|
||||||
enumerable: true,
|
|
||||||
writable: true,
|
|
||||||
value: function(sessionType, path, method, payloadObj, callback) {
|
|
||||||
// XXX Should really return a DOM promise here.
|
|
||||||
MozLoopService.hawkRequest(sessionType, path, method, payloadObj).then((response) => {
|
|
||||||
invokeCallback(callback, null, response.body);
|
|
||||||
}, hawkError => {
|
|
||||||
// The hawkError.error property, while usually a string representing
|
|
||||||
// an HTTP response status message, may also incorrectly be a native
|
|
||||||
// error object that will cause the cloning function to fail.
|
|
||||||
invokeCallback(callback, Cu.cloneInto({
|
|
||||||
error: (hawkError.error && typeof hawkError.error == "string")
|
|
||||||
? hawkError.error : "Unexpected exception",
|
|
||||||
message: hawkError.message,
|
|
||||||
code: hawkError.code,
|
|
||||||
errno: hawkError.errno,
|
|
||||||
}, targetWindow));
|
|
||||||
}).catch(Cu.reportError);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
LOOP_SESSION_TYPE: {
|
LOOP_SESSION_TYPE: {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
get: function() {
|
get: function() {
|
||||||
|
@ -153,9 +153,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "LoopContacts",
|
|||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
|
XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
|
||||||
"resource:///modules/loop/LoopStorage.jsm");
|
"resource:///modules/loop/LoopStorage.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopCalls",
|
|
||||||
"resource:///modules/loop/LoopCalls.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopRooms",
|
XPCOMUtils.defineLazyModuleGetter(this, "LoopRooms",
|
||||||
"resource:///modules/loop/LoopRooms.jsm");
|
"resource:///modules/loop/LoopRooms.jsm");
|
||||||
|
|
||||||
@ -410,7 +407,7 @@ var MozLoopServiceInternal = {
|
|||||||
* @param {String} channelID Unique identifier for the notification channel
|
* @param {String} channelID Unique identifier for the notification channel
|
||||||
* registered with the PushServer.
|
* registered with the PushServer.
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType
|
* @param {LOOP_SESSION_TYPE} sessionType
|
||||||
* @param {String} serviceType Either 'calls' or 'rooms'.
|
* @param {String} serviceType Only 'rooms' is supported.
|
||||||
* @param {Function} onNotification Callback function that will be associated
|
* @param {Function} onNotification Callback function that will be associated
|
||||||
* with this channel from the PushServer.
|
* with this channel from the PushServer.
|
||||||
* @returns {Promise} A promise that is resolved with no params on completion, or
|
* @returns {Promise} A promise that is resolved with no params on completion, or
|
||||||
@ -464,12 +461,8 @@ var MozLoopServiceInternal = {
|
|||||||
roomsPushNotification);
|
roomsPushNotification);
|
||||||
} else {
|
} else {
|
||||||
regPromise = this.createNotificationChannel(
|
regPromise = this.createNotificationChannel(
|
||||||
MozLoopService.channelIDs.callsFxA, sessionType, "calls",
|
MozLoopService.channelIDs.roomsFxA, sessionType, "rooms",
|
||||||
LoopCalls.onNotification).then(() => {
|
roomsPushNotification);
|
||||||
return this.createNotificationChannel(
|
|
||||||
MozLoopService.channelIDs.roomsFxA, sessionType, "rooms",
|
|
||||||
roomsPushNotification);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("assigning to deferredRegistrations for sessionType:", sessionType);
|
log.debug("assigning to deferredRegistrations for sessionType:", sessionType);
|
||||||
@ -491,7 +484,7 @@ var MozLoopServiceInternal = {
|
|||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
||||||
* @param {String} serviceType: "rooms" or "calls"
|
* @param {String} serviceType: only "rooms" is currently supported.
|
||||||
* @param {Boolean} [retry=true] Whether to retry if authentication fails.
|
* @param {Boolean} [retry=true] Whether to retry if authentication fails.
|
||||||
* @return {Promise} resolves to pushURL or rejects with an Error
|
* @return {Promise} resolves to pushURL or rejects with an Error
|
||||||
*/
|
*/
|
||||||
@ -505,7 +498,7 @@ var MozLoopServiceInternal = {
|
|||||||
|
|
||||||
// Create a blank URL record set if none exists for this sessionType.
|
// Create a blank URL record set if none exists for this sessionType.
|
||||||
if (!pushURLs) {
|
if (!pushURLs) {
|
||||||
pushURLs = { calls: undefined, rooms: undefined };
|
pushURLs = { rooms: undefined };
|
||||||
this.pushURLs.set(sessionType, pushURLs);
|
this.pushURLs.set(sessionType, pushURLs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -513,8 +506,7 @@ var MozLoopServiceInternal = {
|
|||||||
return Promise.resolve(pushURL);
|
return Promise.resolve(pushURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
let newURLs = {calls: pushURLs.calls,
|
let newURLs = {rooms: pushURLs.rooms};
|
||||||
rooms: pushURLs.rooms};
|
|
||||||
newURLs[serviceType] = pushURL;
|
newURLs[serviceType] = pushURL;
|
||||||
|
|
||||||
return this.hawkRequestInternal(sessionType, "/registration", "POST",
|
return this.hawkRequestInternal(sessionType, "/registration", "POST",
|
||||||
@ -555,7 +547,7 @@ var MozLoopServiceInternal = {
|
|||||||
* guest session with the device.
|
* guest session with the device.
|
||||||
*
|
*
|
||||||
* NOTE: It is the responsibiliy of the caller the clear the session token
|
* NOTE: It is the responsibiliy of the caller the clear the session token
|
||||||
* after all of the notification classes: calls and rooms, for either
|
* after all of the notification classes: rooms, for either
|
||||||
* Guest or FxA have been unregistered with the LoopServer.
|
* Guest or FxA have been unregistered with the LoopServer.
|
||||||
*
|
*
|
||||||
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
* @param {LOOP_SESSION_TYPE} sessionType The type of session e.g. guest or FxA
|
||||||
@ -570,34 +562,29 @@ var MozLoopServiceInternal = {
|
|||||||
|
|
||||||
let error,
|
let error,
|
||||||
pushURLs = this.pushURLs.get(sessionType),
|
pushURLs = this.pushURLs.get(sessionType),
|
||||||
callsPushURL = pushURLs ? pushURLs.calls : null,
|
|
||||||
roomsPushURL = pushURLs ? pushURLs.rooms : null;
|
roomsPushURL = pushURLs ? pushURLs.rooms : null;
|
||||||
this.pushURLs.delete(sessionType);
|
this.pushURLs.delete(sessionType);
|
||||||
|
|
||||||
let unregister = (sessType, pushURL) => {
|
if (!roomsPushURL) {
|
||||||
if (!pushURL) {
|
return Promise.resolve("no pushURL of this type to unregister");
|
||||||
return Promise.resolve("no pushURL of this type to unregister");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let unregisterURL = "/registration?simplePushURL=" + encodeURIComponent(pushURL);
|
let unregisterURL = "/registration?simplePushURL=" + encodeURIComponent(roomsPushURL);
|
||||||
return this.hawkRequestInternal(sessType, unregisterURL, "DELETE").then(
|
return this.hawkRequestInternal(sessionType, unregisterURL, "DELETE").then(
|
||||||
() => {
|
() => {
|
||||||
log.debug("Successfully unregistered from server for sessionType = ", sessType);
|
log.debug("Successfully unregistered from server for sessionType = ", sessionType);
|
||||||
return "unregistered sessionType " + sessType;
|
return "unregistered sessionType " + sessionType;
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
if (err.code === 401) {
|
if (err.code === 401) {
|
||||||
// Authorization failed, invalid token. This is fine since it may mean we already logged out.
|
// Authorization failed, invalid token. This is fine since it may mean we already logged out.
|
||||||
log.debug("already unregistered - invalid token", sessType);
|
log.debug("already unregistered - invalid token", sessionType);
|
||||||
return "already unregistered, sessionType = " + sessType;
|
return "already unregistered, sessionType = " + sessionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.error("Failed to unregister with the loop server. Error: ", error);
|
log.error("Failed to unregister with the loop server. Error: ", error);
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
return Promise.all([unregister(sessionType, callsPushURL), unregister(sessionType, roomsPushURL)]);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -874,16 +861,13 @@ var MozLoopServiceInternal = {
|
|||||||
}, pc.id);
|
}, pc.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an id for the chat window, for now we just use the roomToken.
|
||||||
|
*
|
||||||
|
* @param {Object} conversationWindowData The conversation window data.
|
||||||
|
*/
|
||||||
getChatWindowID: function(conversationWindowData) {
|
getChatWindowID: function(conversationWindowData) {
|
||||||
// Try getting a window ID that can (re-)identify this conversation, or resort
|
return conversationWindowData.roomToken;
|
||||||
// to a globally unique one as a last resort.
|
|
||||||
// XXX We can clean this up once rooms and direct contact calling are the only
|
|
||||||
// two modes left.
|
|
||||||
let windowId = ("contact" in conversationWindowData) ?
|
|
||||||
conversationWindowData.contact._guid || gLastWindowId++ :
|
|
||||||
conversationWindowData.roomToken || conversationWindowData.callId ||
|
|
||||||
gLastWindowId++;
|
|
||||||
return windowId.toString();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getChatURL: function(chatWindowId) {
|
getChatURL: function(chatWindowId) {
|
||||||
@ -1203,7 +1187,6 @@ this.MozLoopService = {
|
|||||||
get channelIDs() {
|
get channelIDs() {
|
||||||
// Channel ids that will be registered with the PushServer for notifications
|
// Channel ids that will be registered with the PushServer for notifications
|
||||||
return {
|
return {
|
||||||
callsFxA: "25389583-921f-4169-a426-a4673658944b",
|
|
||||||
roomsFxA: "6add272a-d316-477c-8335-f00f73dfde71",
|
roomsFxA: "6add272a-d316-477c-8335-f00f73dfde71",
|
||||||
roomsGuest: "19d3f799-a8f3-4328-9822-b7cd02765832"
|
roomsGuest: "19d3f799-a8f3-4328-9822-b7cd02765832"
|
||||||
};
|
};
|
||||||
@ -1697,7 +1680,6 @@ this.MozLoopService = {
|
|||||||
MozLoopServiceInternal.deferredRegistrations.delete(LOOP_SESSION_TYPE.FXA);
|
MozLoopServiceInternal.deferredRegistrations.delete(LOOP_SESSION_TYPE.FXA);
|
||||||
// Unregister with PushHandler so these push channels will not get re-registered
|
// Unregister with PushHandler so these push channels will not get re-registered
|
||||||
// if the connection is re-established by the PushHandler.
|
// if the connection is re-established by the PushHandler.
|
||||||
MozLoopServiceInternal.pushHandler.unregister(MozLoopService.channelIDs.callsFxA);
|
|
||||||
MozLoopServiceInternal.pushHandler.unregister(MozLoopService.channelIDs.roomsFxA);
|
MozLoopServiceInternal.pushHandler.unregister(MozLoopService.channelIDs.roomsFxA);
|
||||||
|
|
||||||
// Reset the client since the initial promiseFxAOAuthParameters() call is
|
// Reset the client since the initial promiseFxAOAuthParameters() call is
|
||||||
@ -1868,8 +1850,8 @@ this.MozLoopService = {
|
|||||||
/**
|
/**
|
||||||
* Returns the window data for a specific conversation window id.
|
* Returns the window data for a specific conversation window id.
|
||||||
*
|
*
|
||||||
* This data will be relevant to the type of window, e.g. rooms or calls.
|
* This data will be relevant to the type of window, e.g. rooms.
|
||||||
* See LoopRooms or LoopCalls for more information.
|
* See LoopRooms for more information.
|
||||||
*
|
*
|
||||||
* @param {String} conversationWindowId
|
* @param {String} conversationWindowId
|
||||||
* @returns {Object} The window data or null if error.
|
* @returns {Object} The window data or null if error.
|
||||||
|
@ -17,7 +17,6 @@ EXTRA_JS_MODULES.loop += [
|
|||||||
'content/shared/js/utils.js',
|
'content/shared/js/utils.js',
|
||||||
'modules/CardDavImporter.jsm',
|
'modules/CardDavImporter.jsm',
|
||||||
'modules/GoogleImporter.jsm',
|
'modules/GoogleImporter.jsm',
|
||||||
'modules/LoopCalls.jsm',
|
|
||||||
'modules/LoopContacts.jsm',
|
'modules/LoopContacts.jsm',
|
||||||
'modules/LoopRooms.jsm',
|
'modules/LoopRooms.jsm',
|
||||||
'modules/LoopRoomsCache.jsm',
|
'modules/LoopRoomsCache.jsm',
|
||||||
|
@ -313,8 +313,6 @@ add_task(function* basicAuthorizationAndRegistration() {
|
|||||||
is(loopButton.getAttribute("state"), "active", "state of loop button should be active when logged in");
|
is(loopButton.getAttribute("state"), "active", "state of loop button should be active when logged in");
|
||||||
|
|
||||||
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||||
is(registrationResponse.response.simplePushURLs.calls, "https://localhost/pushUrl/fxa-calls",
|
|
||||||
"Check registered push URL");
|
|
||||||
is(registrationResponse.response.simplePushURLs.rooms, "https://localhost/pushUrl/fxa-rooms",
|
is(registrationResponse.response.simplePushURLs.rooms, "https://localhost/pushUrl/fxa-rooms",
|
||||||
"Check registered push URL");
|
"Check registered push URL");
|
||||||
|
|
||||||
@ -367,10 +365,10 @@ add_task(function* logoutWithIncorrectPushURL() {
|
|||||||
// Create a fake FxA hawk session token
|
// Create a fake FxA hawk session token
|
||||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||||
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
||||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "calls", pushURL);
|
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "rooms", pushURL);
|
||||||
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||||
is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
|
is(registrationResponse.response.simplePushURLs.rooms, pushURL, "Check registered push URL");
|
||||||
MozLoopServiceInternal.pushURLs.get(LOOP_SESSION_TYPE.FXA).calls = "http://www.example.com/invalid";
|
MozLoopServiceInternal.pushURLs.get(LOOP_SESSION_TYPE.FXA).rooms = "http://www.example.com/invalid";
|
||||||
let caught = false;
|
let caught = false;
|
||||||
yield MozLoopService.logOutFromFxA().catch((error) => {
|
yield MozLoopService.logOutFromFxA().catch((error) => {
|
||||||
caught = true;
|
caught = true;
|
||||||
@ -378,7 +376,7 @@ add_task(function* logoutWithIncorrectPushURL() {
|
|||||||
ok(caught, "Should have caught an error logging out with a mismatched push URL");
|
ok(caught, "Should have caught an error logging out with a mismatched push URL");
|
||||||
checkLoggedOutState();
|
checkLoggedOutState();
|
||||||
registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||||
is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
|
is(registrationResponse.response.simplePushURLs.rooms, pushURL, "Check registered push URL wasn't deleted");
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function* logoutWithNoPushURL() {
|
add_task(function* logoutWithNoPushURL() {
|
||||||
@ -388,14 +386,14 @@ add_task(function* logoutWithNoPushURL() {
|
|||||||
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
|
||||||
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
Services.prefs.setCharPref(fxASessionPref, "X".repeat(HAWK_TOKEN_LENGTH));
|
||||||
|
|
||||||
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "calls", pushURL);
|
yield MozLoopServiceInternal.registerWithLoopServer(LOOP_SESSION_TYPE.FXA, "rooms", pushURL);
|
||||||
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||||
is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL");
|
is(registrationResponse.response.simplePushURLs.rooms, pushURL, "Check registered push URL");
|
||||||
MozLoopServiceInternal.pushURLs.delete(LOOP_SESSION_TYPE.FXA);
|
MozLoopServiceInternal.pushURLs.delete(LOOP_SESSION_TYPE.FXA);
|
||||||
yield MozLoopService.logOutFromFxA();
|
yield MozLoopService.logOutFromFxA();
|
||||||
checkLoggedOutState();
|
checkLoggedOutState();
|
||||||
registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
|
||||||
is(registrationResponse.response.simplePushURLs.calls, pushURL, "Check registered push URL wasn't deleted");
|
is(registrationResponse.response.simplePushURLs.rooms, pushURL, "Check registered push URL wasn't deleted");
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function* loginWithRegistration401() {
|
add_task(function* loginWithRegistration401() {
|
||||||
|
@ -9,7 +9,6 @@ const {
|
|||||||
MozLoopServiceInternal,
|
MozLoopServiceInternal,
|
||||||
MozLoopService
|
MozLoopService
|
||||||
} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||||
const {LoopCalls} = Cu.import("resource:///modules/loop/LoopCalls.jsm", {});
|
|
||||||
const {LoopRooms} = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
|
const {LoopRooms} = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
|
||||||
|
|
||||||
// Cache this value only once, at the beginning of a
|
// Cache this value only once, at the beginning of a
|
||||||
@ -231,12 +230,8 @@ var mockPushHandler = {
|
|||||||
if ("mockWebSocket" in options) {
|
if ("mockWebSocket" in options) {
|
||||||
this._mockWebSocket = options.mockWebSocket;
|
this._mockWebSocket = options.mockWebSocket;
|
||||||
}
|
}
|
||||||
this.registrationPushURLs[MozLoopService.channelIDs.callsGuest] =
|
|
||||||
"https://localhost/pushUrl/guest-calls";
|
|
||||||
this.registrationPushURLs[MozLoopService.channelIDs.roomsGuest] =
|
this.registrationPushURLs[MozLoopService.channelIDs.roomsGuest] =
|
||||||
"https://localhost/pushUrl/guest-rooms";
|
"https://localhost/pushUrl/guest-rooms";
|
||||||
this.registrationPushURLs[MozLoopService.channelIDs.callsFxA] =
|
|
||||||
"https://localhost/pushUrl/fxa-calls";
|
|
||||||
this.registrationPushURLs[MozLoopService.channelIDs.roomsFxA] =
|
this.registrationPushURLs[MozLoopService.channelIDs.roomsFxA] =
|
||||||
"https://localhost/pushUrl/fxa-rooms";
|
"https://localhost/pushUrl/fxa-rooms";
|
||||||
},
|
},
|
||||||
|
@ -213,7 +213,6 @@ function push_server(request, response) {
|
|||||||
function registration(request, response) {
|
function registration(request, response) {
|
||||||
let isFxARequest = function(payload) {
|
let isFxARequest = function(payload) {
|
||||||
return (payload.simplePushURL == "https://localhost/pushUrl/fxa" ||
|
return (payload.simplePushURL == "https://localhost/pushUrl/fxa" ||
|
||||||
payload.simplePushURLs.calls == "https://localhost/pushUrl/fxa-calls" ||
|
|
||||||
payload.simplePushURLs.rooms == "https://localhost/pushUrl/fxa-rooms");
|
payload.simplePushURLs.rooms == "https://localhost/pushUrl/fxa-rooms");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -254,9 +253,9 @@ function delete_registration(request, response) {
|
|||||||
// registering endpoints at the root of the hostname e.g. /registration.
|
// registering endpoints at the root of the hostname e.g. /registration.
|
||||||
let url = new URL(request.queryString.replace(/%3F.*/,""), "http://www.example.com");
|
let url = new URL(request.queryString.replace(/%3F.*/,""), "http://www.example.com");
|
||||||
let state = getSharedState("/registration");
|
let state = getSharedState("/registration");
|
||||||
if (state != "") { //Already set to empty value on a successful channel unregsitration.
|
if (state != "") { //Already set to empty value on a successful channel unregistration.
|
||||||
let registration = JSON.parse(state);
|
let registration = JSON.parse(state);
|
||||||
if (registration.simplePushURLs.calls == url.searchParams.get("simplePushURL")) {
|
if (registration.simplePushURLs.rooms == url.searchParams.get("simplePushURL")) {
|
||||||
setSharedState("/registration", "");
|
setSharedState("/registration", "");
|
||||||
} else {
|
} else {
|
||||||
response.setStatusLine(request.httpVersion, 400, "Bad Request");
|
response.setStatusLine(request.httpVersion, 400, "Bad Request");
|
||||||
|
@ -66,16 +66,16 @@ describe("loop.Dispatcher", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("#dispatch", function() {
|
describe("#dispatch", function() {
|
||||||
var getDataStore1, getDataStore2, cancelStore1, connectStore1;
|
var getDataStore1, getDataStore2, gotMediaPermissionStore1, mediaConnectedStore1;
|
||||||
var getDataAction, cancelAction, connectAction, resolveCancelStore1;
|
var getDataAction, gotMediaPermissionAction, mediaConnectedAction;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
getDataAction = new sharedActions.GetWindowData({
|
getDataAction = new sharedActions.GetWindowData({
|
||||||
windowId: "42"
|
windowId: "42"
|
||||||
});
|
});
|
||||||
|
|
||||||
cancelAction = new sharedActions.CancelCall();
|
gotMediaPermissionAction = new sharedActions.GotMediaPermission();
|
||||||
connectAction = new sharedActions.ConnectCall({
|
mediaConnectedAction = new sharedActions.MediaConnected({
|
||||||
sessionData: {}
|
sessionData: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -85,26 +85,27 @@ describe("loop.Dispatcher", function () {
|
|||||||
getDataStore2 = {
|
getDataStore2 = {
|
||||||
getWindowData: sinon.stub()
|
getWindowData: sinon.stub()
|
||||||
};
|
};
|
||||||
cancelStore1 = {
|
gotMediaPermissionStore1 = {
|
||||||
cancelCall: sinon.stub()
|
gotMediaPermission: sinon.stub()
|
||||||
};
|
};
|
||||||
connectStore1 = {
|
mediaConnectedStore1 = {
|
||||||
connectCall: function() {}
|
mediaConnected: function() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatcher.register(getDataStore1, ["getWindowData"]);
|
dispatcher.register(getDataStore1, ["getWindowData"]);
|
||||||
dispatcher.register(getDataStore2, ["getWindowData"]);
|
dispatcher.register(getDataStore2, ["getWindowData"]);
|
||||||
dispatcher.register(cancelStore1, ["cancelCall"]);
|
dispatcher.register(gotMediaPermissionStore1, ["gotMediaPermission"]);
|
||||||
dispatcher.register(connectStore1, ["connectCall"]);
|
dispatcher.register(mediaConnectedStore1, ["mediaConnected"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch an action to the required object", function() {
|
it("should dispatch an action to the required object", function() {
|
||||||
dispatcher.dispatch(cancelAction);
|
dispatcher.dispatch(gotMediaPermissionAction);
|
||||||
|
|
||||||
sinon.assert.notCalled(getDataStore1.getWindowData);
|
sinon.assert.notCalled(getDataStore1.getWindowData);
|
||||||
|
|
||||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
sinon.assert.calledOnce(gotMediaPermissionStore1.gotMediaPermission);
|
||||||
sinon.assert.calledWithExactly(cancelStore1.cancelCall, cancelAction);
|
sinon.assert.calledWithExactly(gotMediaPermissionStore1.gotMediaPermission,
|
||||||
|
gotMediaPermissionAction);
|
||||||
|
|
||||||
sinon.assert.notCalled(getDataStore2.getWindowData);
|
sinon.assert.notCalled(getDataStore2.getWindowData);
|
||||||
});
|
});
|
||||||
@ -115,17 +116,17 @@ describe("loop.Dispatcher", function () {
|
|||||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||||
sinon.assert.calledWithExactly(getDataStore1.getWindowData, getDataAction);
|
sinon.assert.calledWithExactly(getDataStore1.getWindowData, getDataAction);
|
||||||
|
|
||||||
sinon.assert.notCalled(cancelStore1.cancelCall);
|
sinon.assert.notCalled(gotMediaPermissionStore1.gotMediaPermission);
|
||||||
|
|
||||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||||
sinon.assert.calledWithExactly(getDataStore2.getWindowData, getDataAction);
|
sinon.assert.calledWithExactly(getDataStore2.getWindowData, getDataAction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch multiple actions", function() {
|
it("should dispatch multiple actions", function() {
|
||||||
dispatcher.dispatch(cancelAction);
|
dispatcher.dispatch(gotMediaPermissionAction);
|
||||||
dispatcher.dispatch(getDataAction);
|
dispatcher.dispatch(getDataAction);
|
||||||
|
|
||||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
sinon.assert.calledOnce(gotMediaPermissionStore1.gotMediaPermission);
|
||||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||||
});
|
});
|
||||||
@ -139,11 +140,11 @@ describe("loop.Dispatcher", function () {
|
|||||||
getDataStore1.getWindowData.throws("Uncaught Error");
|
getDataStore1.getWindowData.throws("Uncaught Error");
|
||||||
|
|
||||||
dispatcher.dispatch(getDataAction);
|
dispatcher.dispatch(getDataAction);
|
||||||
dispatcher.dispatch(cancelAction);
|
dispatcher.dispatch(gotMediaPermissionAction);
|
||||||
|
|
||||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
sinon.assert.calledOnce(gotMediaPermissionStore1.gotMediaPermission);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should log uncaught exceptions", function() {
|
it("should log uncaught exceptions", function() {
|
||||||
@ -159,7 +160,7 @@ describe("loop.Dispatcher", function () {
|
|||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
// Restore the stub, so that we can easily add a function to be
|
// Restore the stub, so that we can easily add a function to be
|
||||||
// returned. Unfortunately, sinon doesn't make this easy.
|
// returned. Unfortunately, sinon doesn't make this easy.
|
||||||
sandbox.stub(connectStore1, "connectCall", function() {
|
sandbox.stub(mediaConnectedStore1, "mediaConnected", function() {
|
||||||
dispatcher.dispatch(getDataAction);
|
dispatcher.dispatch(getDataAction);
|
||||||
|
|
||||||
sinon.assert.notCalled(getDataStore1.getWindowData);
|
sinon.assert.notCalled(getDataStore1.getWindowData);
|
||||||
@ -170,17 +171,17 @@ describe("loop.Dispatcher", function () {
|
|||||||
it("should not dispatch an action if the previous action hasn't finished", function() {
|
it("should not dispatch an action if the previous action hasn't finished", function() {
|
||||||
// Dispatch the first action. The action handler dispatches the second
|
// Dispatch the first action. The action handler dispatches the second
|
||||||
// action - see the beforeEach above.
|
// action - see the beforeEach above.
|
||||||
dispatcher.dispatch(connectAction);
|
dispatcher.dispatch(mediaConnectedAction);
|
||||||
|
|
||||||
sinon.assert.calledOnce(connectStore1.connectCall);
|
sinon.assert.calledOnce(mediaConnectedStore1.mediaConnected);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch an action when the previous action finishes", function() {
|
it("should dispatch an action when the previous action finishes", function() {
|
||||||
// Dispatch the first action. The action handler dispatches the second
|
// Dispatch the first action. The action handler dispatches the second
|
||||||
// action - see the beforeEach above.
|
// action - see the beforeEach above.
|
||||||
dispatcher.dispatch(connectAction);
|
dispatcher.dispatch(mediaConnectedAction);
|
||||||
|
|
||||||
sinon.assert.calledOnce(connectStore1.connectCall);
|
sinon.assert.calledOnce(mediaConnectedStore1.mediaConnected);
|
||||||
// These should be called, because the dispatcher synchronously queues actions.
|
// These should be called, because the dispatcher synchronously queues actions.
|
||||||
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
sinon.assert.calledOnce(getDataStore1.getWindowData);
|
||||||
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
sinon.assert.calledOnce(getDataStore2.getWindowData);
|
||||||
|
@ -14,7 +14,6 @@ Cu.import("resource://gre/modules/Http.jsm");
|
|||||||
Cu.import("resource://testing-common/httpd.js");
|
Cu.import("resource://testing-common/httpd.js");
|
||||||
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
Cu.import("resource://gre/modules/Promise.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
|
||||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||||
Cu.import("resource://gre/modules/osfile.jsm");
|
Cu.import("resource://gre/modules/osfile.jsm");
|
||||||
const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unit tests for the hawkRequest API
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
Cu.import("resource:///modules/loop/MozLoopAPI.jsm");
|
|
||||||
|
|
||||||
var sandbox;
|
|
||||||
function assertInSandbox(expr, msg_opt) {
|
|
||||||
Assert.ok(Cu.evalInSandbox(expr, sandbox), msg_opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
sandbox = Cu.Sandbox("about:looppanel", { wantXrays: false } );
|
|
||||||
injectLoopAPI(sandbox, true);
|
|
||||||
|
|
||||||
add_task(function* hawk_session_scope_constants() {
|
|
||||||
assertInSandbox("typeof mozLoop.LOOP_SESSION_TYPE !== 'undefined'");
|
|
||||||
|
|
||||||
assertInSandbox("mozLoop.LOOP_SESSION_TYPE.GUEST === 1");
|
|
||||||
|
|
||||||
assertInSandbox("mozLoop.LOOP_SESSION_TYPE.FXA === 2");
|
|
||||||
});
|
|
||||||
|
|
||||||
function generateSessionTypeVerificationStub(desiredSessionType) {
|
|
||||||
|
|
||||||
function hawkRequestStub(sessionType, path, method, payloadObj, callback) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
Assert.equal(desiredSessionType, sessionType);
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return hawkRequestStub;
|
|
||||||
}
|
|
||||||
|
|
||||||
const origHawkRequest = MozLoopService.hawkRequest;
|
|
||||||
do_register_cleanup(function() {
|
|
||||||
MozLoopService.hawkRequest = origHawkRequest;
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function* hawk_request_scope_passthrough() {
|
|
||||||
|
|
||||||
// add a stub that verifies the parameter we want
|
|
||||||
MozLoopService.hawkRequest =
|
|
||||||
generateSessionTypeVerificationStub(sandbox.mozLoop.LOOP_SESSION_TYPE.FXA);
|
|
||||||
|
|
||||||
// call mozLoop.hawkRequest, which calls MozLoopAPI.hawkRequest, which calls
|
|
||||||
// MozLoopService.hawkRequest
|
|
||||||
Cu.evalInSandbox(
|
|
||||||
"mozLoop.hawkRequest(mozLoop.LOOP_SESSION_TYPE.FXA," +
|
|
||||||
" 'call-url/fakeToken', 'POST', {}, function() {})",
|
|
||||||
sandbox);
|
|
||||||
|
|
||||||
MozLoopService.hawkRequest =
|
|
||||||
generateSessionTypeVerificationStub(sandbox.mozLoop.LOOP_SESSION_TYPE.GUEST);
|
|
||||||
|
|
||||||
Cu.evalInSandbox(
|
|
||||||
"mozLoop.hawkRequest(mozLoop.LOOP_SESSION_TYPE.GUEST," +
|
|
||||||
" 'call-url/fakeToken', 'POST', {}, function() {})",
|
|
||||||
sandbox);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function run_test() {
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const { LoopCallsInternal } = Cu.import("resource:///modules/loop/LoopCalls.jsm", {});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
|
|
||||||
"resource:///modules/Chat.jsm");
|
|
||||||
|
|
||||||
var actionReceived = false;
|
|
||||||
var openChatOrig = Chat.open;
|
|
||||||
|
|
||||||
const firstCallId = 4444333221;
|
|
||||||
const secondCallId = 1001100101;
|
|
||||||
|
|
||||||
var msgHandler = function(msg) {
|
|
||||||
if (msg.messageType &&
|
|
||||||
msg.messageType === "action" &&
|
|
||||||
msg.event === "terminate" &&
|
|
||||||
msg.reason === "busy") {
|
|
||||||
actionReceived = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
add_task(function* test_busy_2fxa_calls() {
|
|
||||||
actionReceived = false;
|
|
||||||
|
|
||||||
yield MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA);
|
|
||||||
|
|
||||||
let opened = 0;
|
|
||||||
let windowId;
|
|
||||||
Chat.open = function(contentWindow, origin, title, url) {
|
|
||||||
opened++;
|
|
||||||
windowId = url.match(/about:loopconversation\#(\d+)$/)[1];
|
|
||||||
return windowId;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
|
||||||
|
|
||||||
yield waitForCondition(() => { return actionReceived && opened > 0; }).then(() => {
|
|
||||||
do_check_true(opened === 1, "should open only one chat window");
|
|
||||||
do_check_true(actionReceived, "should respond with busy/reject to second call");
|
|
||||||
LoopCalls.clearCallInProgress(windowId);
|
|
||||||
}, () => {
|
|
||||||
do_throw("should have opened a chat window for first call and rejected second call");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function run_test() {
|
|
||||||
setupFakeLoopServer();
|
|
||||||
|
|
||||||
// Setup fake login state so we get FxA requests.
|
|
||||||
const MozLoopServiceInternal = Cu.import("resource:///modules/loop/MozLoopService.jsm", {}).MozLoopServiceInternal;
|
|
||||||
MozLoopServiceInternal.fxAOAuthTokenData = {
|
|
||||||
token_type: "bearer",
|
|
||||||
access_token: "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
|
|
||||||
scope: "profile"
|
|
||||||
};
|
|
||||||
MozLoopServiceInternal.fxAOAuthProfile = {email: "test@example.com", uid: "abcd1234"};
|
|
||||||
|
|
||||||
let mockWebSocket = new MockWebSocketChannel();
|
|
||||||
mockWebSocket.defaultMsgHandler = msgHandler;
|
|
||||||
LoopCallsInternal.mocks.webSocket = mockWebSocket;
|
|
||||||
|
|
||||||
Services.io.offline = false;
|
|
||||||
|
|
||||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
|
||||||
|
|
||||||
// For each notification received from the PushServer, MozLoopService will first query
|
|
||||||
// for any pending calls on the FxA hawk session and then again using the guest session.
|
|
||||||
// A pair of response objects in the callsResponses array will be consumed for each
|
|
||||||
// notification. The even calls object is for the FxA session, the odd the Guest session.
|
|
||||||
|
|
||||||
let callsRespCount = 0;
|
|
||||||
let callsResponses = [
|
|
||||||
{calls: [{callId: firstCallId,
|
|
||||||
websocketToken: "0deadbeef0",
|
|
||||||
progressURL: "wss://localhost:5000/websocket"},
|
|
||||||
{callId: secondCallId,
|
|
||||||
websocketToken: "1deadbeef1",
|
|
||||||
progressURL: "wss://localhost:5000/websocket"}]},
|
|
||||||
{calls: [{callId: firstCallId,
|
|
||||||
websocketToken: "0deadbeef0",
|
|
||||||
progressURL: "wss://localhost:5000/websocket"}]},
|
|
||||||
{calls: [{callId: secondCallId,
|
|
||||||
websocketToken: "1deadbeef1",
|
|
||||||
progressURL: "wss://localhost:5000/websocket"}]}
|
|
||||||
];
|
|
||||||
|
|
||||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
|
||||||
response.setStatusLine(null, 200, "OK");
|
|
||||||
response.processAsync();
|
|
||||||
response.finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
loopServer.registerPathHandler("/calls", (request, response) => {
|
|
||||||
response.setStatusLine(null, 200, "OK");
|
|
||||||
|
|
||||||
if (callsRespCount >= callsResponses.length) {
|
|
||||||
callsRespCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
response.write(JSON.stringify(callsResponses[callsRespCount++]));
|
|
||||||
response.processAsync();
|
|
||||||
response.finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
do_register_cleanup(function() {
|
|
||||||
// Revert original Chat.open implementation
|
|
||||||
Chat.open = openChatOrig;
|
|
||||||
|
|
||||||
// Revert fake login state
|
|
||||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
|
||||||
|
|
||||||
LoopCallsInternal.mocks.webSocket = undefined;
|
|
||||||
});
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
|
|
||||||
"resource:///modules/Chat.jsm");
|
|
||||||
var openChatOrig = Chat.open;
|
|
||||||
|
|
||||||
const contact = {
|
|
||||||
name: [ "Mr Smith" ],
|
|
||||||
email: [{
|
|
||||||
type: "home",
|
|
||||||
value: "fakeEmail",
|
|
||||||
pref: true
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
|
|
||||||
add_task(function test_startDirectCall_opens_window() {
|
|
||||||
let openedUrl;
|
|
||||||
Chat.open = function(contentWindow, origin, title, url) {
|
|
||||||
openedUrl = url;
|
|
||||||
return 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
LoopCalls.startDirectCall(contact, "audio-video");
|
|
||||||
|
|
||||||
do_check_true(!!openedUrl, "should open a chat window");
|
|
||||||
|
|
||||||
// Stop the busy kicking in for following tests.
|
|
||||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
|
||||||
LoopCalls.clearCallInProgress(windowId);
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_startDirectCall_getConversationWindowData() {
|
|
||||||
let openedUrl;
|
|
||||||
Chat.open = function(contentWindow, origin, title, url) {
|
|
||||||
openedUrl = url;
|
|
||||||
return 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
LoopCalls.startDirectCall(contact, "audio-video");
|
|
||||||
|
|
||||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
|
||||||
|
|
||||||
let callData = MozLoopService.getConversationWindowData(windowId);
|
|
||||||
|
|
||||||
do_check_eq(callData.callType, "audio-video", "should have the correct call type");
|
|
||||||
do_check_eq(callData.contact, contact, "should have the contact details");
|
|
||||||
|
|
||||||
// Stop the busy kicking in for following tests.
|
|
||||||
LoopCalls.clearCallInProgress(windowId);
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_startDirectCall_not_busy_if_window_fails_to_open() {
|
|
||||||
let openedUrl;
|
|
||||||
|
|
||||||
// Simulate no window available to open.
|
|
||||||
Chat.open = function(contentWindow, origin, title, url) {
|
|
||||||
openedUrl = url;
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
LoopCalls.startDirectCall(contact, "audio-video");
|
|
||||||
|
|
||||||
do_check_true(!!openedUrl, "should have attempted to open chat window");
|
|
||||||
|
|
||||||
openedUrl = null;
|
|
||||||
|
|
||||||
// Window opens successfully this time.
|
|
||||||
Chat.open = function(contentWindow, origin, title, url) {
|
|
||||||
openedUrl = url;
|
|
||||||
return 3;
|
|
||||||
};
|
|
||||||
|
|
||||||
LoopCalls.startDirectCall(contact, "audio-video");
|
|
||||||
|
|
||||||
do_check_true(!!openedUrl, "should open a chat window");
|
|
||||||
|
|
||||||
// Stop the busy kicking in for following tests.
|
|
||||||
let windowId = openedUrl.match(/about:loopconversation\#(\d+)$/)[1];
|
|
||||||
LoopCalls.clearCallInProgress(windowId);
|
|
||||||
});
|
|
||||||
|
|
||||||
function run_test() {
|
|
||||||
do_register_cleanup(function() {
|
|
||||||
// Revert original Chat.open implementation
|
|
||||||
Chat.open = openChatOrig;
|
|
||||||
});
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
@ -30,44 +30,6 @@ add_test(function test_set_do_not_disturb() {
|
|||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
||||||
add_test(function test_do_not_disturb_disabled_should_open_chat_window() {
|
|
||||||
MozLoopService.doNotDisturb = false;
|
|
||||||
|
|
||||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
|
||||||
|
|
||||||
MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
|
||||||
let opened = false;
|
|
||||||
Chat.open = function() {
|
|
||||||
opened = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
|
||||||
|
|
||||||
waitForCondition(() => opened).then(() => {
|
|
||||||
run_next_test();
|
|
||||||
}, () => {
|
|
||||||
do_throw("should have opened a chat window");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_do_not_disturb_enabled_shouldnt_open_chat_window() {
|
|
||||||
MozLoopService.doNotDisturb = true;
|
|
||||||
|
|
||||||
// We registered in the previous test, so no need to do that on this one.
|
|
||||||
let opened = false;
|
|
||||||
Chat.open = function() {
|
|
||||||
opened = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
|
||||||
|
|
||||||
do_timeout(500, function() {
|
|
||||||
do_check_false(opened, "should not open a chat window");
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function run_test() {
|
function run_test() {
|
||||||
setupFakeLoopServer();
|
setupFakeLoopServer();
|
||||||
|
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Chat",
|
|
||||||
"resource:///modules/Chat.jsm");
|
|
||||||
|
|
||||||
var openChatOrig = Chat.open;
|
|
||||||
|
|
||||||
add_test(function test_openChatWindow_on_notification() {
|
|
||||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
|
||||||
|
|
||||||
MozLoopService.promiseRegisteredWithServers(LOOP_SESSION_TYPE.FXA).then(() => {
|
|
||||||
let opened = false;
|
|
||||||
Chat.open = function() {
|
|
||||||
opened = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
mockPushHandler.notify(1, MozLoopService.channelIDs.callsFxA);
|
|
||||||
|
|
||||||
waitForCondition(() => opened).then(() => {
|
|
||||||
do_check_true(opened, "should open a chat window");
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
}, () => {
|
|
||||||
do_throw("should have opened a chat window");
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function run_test() {
|
|
||||||
setupFakeLoopServer();
|
|
||||||
|
|
||||||
setupFakeFxAUserProfile();
|
|
||||||
|
|
||||||
loopServer.registerPathHandler("/registration", (request, response) => {
|
|
||||||
response.setStatusLine(null, 200, "OK");
|
|
||||||
response.processAsync();
|
|
||||||
response.finish();
|
|
||||||
});
|
|
||||||
loopServer.registerPathHandler("/calls", (request, response) => {
|
|
||||||
response.setStatusLine(null, 200, "OK");
|
|
||||||
response.write(JSON.stringify({calls: [{callId: 4444333221, websocketToken: "0deadbeef0"}]}));
|
|
||||||
response.processAsync();
|
|
||||||
response.finish();
|
|
||||||
});
|
|
||||||
|
|
||||||
do_register_cleanup(function() {
|
|
||||||
// Revert original Chat.open implementation
|
|
||||||
Chat.open = openChatOrig;
|
|
||||||
});
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
@ -4,13 +4,11 @@ tail =
|
|||||||
firefox-appdir = browser
|
firefox-appdir = browser
|
||||||
skip-if = toolkit == 'gonk'
|
skip-if = toolkit == 'gonk'
|
||||||
|
|
||||||
[test_loopapi_hawk_request.js]
|
|
||||||
[test_looppush_initialize.js]
|
[test_looppush_initialize.js]
|
||||||
[test_looprooms.js]
|
[test_looprooms.js]
|
||||||
[test_looprooms_encryption_in_fxa.js]
|
[test_looprooms_encryption_in_fxa.js]
|
||||||
[test_looprooms_first_notification.js]
|
[test_looprooms_first_notification.js]
|
||||||
[test_looprooms_upgrade_to_encryption.js]
|
[test_looprooms_upgrade_to_encryption.js]
|
||||||
[test_loopservice_directcall.js]
|
|
||||||
[test_loopservice_dnd.js]
|
[test_loopservice_dnd.js]
|
||||||
[test_loopservice_encryptionkey.js]
|
[test_loopservice_encryptionkey.js]
|
||||||
[test_loopservice_hawk_errors.js]
|
[test_loopservice_hawk_errors.js]
|
||||||
@ -18,7 +16,6 @@ skip-if = toolkit == 'gonk'
|
|||||||
[test_loopservice_loop_prefs.js]
|
[test_loopservice_loop_prefs.js]
|
||||||
[test_loopservice_initialize.js]
|
[test_loopservice_initialize.js]
|
||||||
[test_loopservice_locales.js]
|
[test_loopservice_locales.js]
|
||||||
[test_loopservice_notification.js]
|
|
||||||
[test_loopservice_registration.js]
|
[test_loopservice_registration.js]
|
||||||
[test_loopservice_registration_retry.js]
|
[test_loopservice_registration_retry.js]
|
||||||
[test_loopservice_restart.js]
|
[test_loopservice_restart.js]
|
||||||
@ -26,4 +23,3 @@ skip-if = toolkit == 'gonk'
|
|||||||
[test_loopservice_token_save.js]
|
[test_loopservice_token_save.js]
|
||||||
[test_loopservice_token_send.js]
|
[test_loopservice_token_send.js]
|
||||||
[test_loopservice_token_validation.js]
|
[test_loopservice_token_validation.js]
|
||||||
[test_loopservice_busy.js]
|
|
||||||
|
@ -501,7 +501,9 @@ nsBrowserContentHandler.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var override;
|
||||||
var overridePage = "";
|
var overridePage = "";
|
||||||
|
var additionalPage = "";
|
||||||
var willRestoreSession = false;
|
var willRestoreSession = false;
|
||||||
try {
|
try {
|
||||||
// Read the old value of homepage_override.mstone before
|
// Read the old value of homepage_override.mstone before
|
||||||
@ -513,12 +515,13 @@ nsBrowserContentHandler.prototype = {
|
|||||||
try {
|
try {
|
||||||
old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
|
old_mstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone");
|
||||||
} catch (ex) {}
|
} catch (ex) {}
|
||||||
let override = needHomepageOverride(prefb);
|
override = needHomepageOverride(prefb);
|
||||||
if (override != OVERRIDE_NONE) {
|
if (override != OVERRIDE_NONE) {
|
||||||
switch (override) {
|
switch (override) {
|
||||||
case OVERRIDE_NEW_PROFILE:
|
case OVERRIDE_NEW_PROFILE:
|
||||||
// New profile.
|
// New profile.
|
||||||
overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
|
overridePage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url");
|
||||||
|
additionalPage = Services.urlFormatter.formatURLPref("startup.homepage_welcome_url.additional");
|
||||||
break;
|
break;
|
||||||
case OVERRIDE_NEW_MSTONE:
|
case OVERRIDE_NEW_MSTONE:
|
||||||
// Check whether we will restore a session. If we will, we assume
|
// Check whether we will restore a session. If we will, we assume
|
||||||
@ -553,14 +556,21 @@ nsBrowserContentHandler.prototype = {
|
|||||||
let firstUseOnWindows10URL = Services.urlFormatter.formatURLPref("browser.usedOnWindows10.introURL");
|
let firstUseOnWindows10URL = Services.urlFormatter.formatURLPref("browser.usedOnWindows10.introURL");
|
||||||
|
|
||||||
if (firstUseOnWindows10URL && firstUseOnWindows10URL.length) {
|
if (firstUseOnWindows10URL && firstUseOnWindows10URL.length) {
|
||||||
if (overridePage) {
|
additionalPage = firstUseOnWindows10URL;
|
||||||
overridePage += "|" + firstUseOnWindows10URL;
|
if (override == OVERRIDE_NEW_PROFILE) {
|
||||||
} else {
|
additionalPage += "&utm_content=firstrun";
|
||||||
overridePage = firstUseOnWindows10URL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (additionalPage && additionalPage != "about:blank") {
|
||||||
|
if (overridePage) {
|
||||||
|
overridePage += "|" + additionalPage;
|
||||||
|
} else {
|
||||||
|
overridePage = additionalPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var startPage = "";
|
var startPage = "";
|
||||||
try {
|
try {
|
||||||
var choice = prefb.getIntPref("browser.startup.page");
|
var choice = prefb.getIntPref("browser.startup.page");
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"clang_version": "r247539"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 105219872,
|
"size": 106877168,
|
||||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.xz",
|
"filename": "clang.tar.xz",
|
||||||
"unpack": true,
|
"unpack": true,
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"clang_version": "r247539"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 105219872,
|
"size": 106877168,
|
||||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.xz",
|
"filename": "clang.tar.xz",
|
||||||
"unpack": true
|
"unpack": true
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"clang_version": "r247539"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 105219872,
|
"size": 106877168,
|
||||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.xz",
|
"filename": "clang.tar.xz",
|
||||||
"unpack": true
|
"unpack": true
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"clang_version": "r247539"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 121393888,
|
"size": 121389802,
|
||||||
"digest": "6ae4e651e545538e6de326a7fb8b44b6e6d0b3acdb6a969ecb2b6f63b9995bbad2111cabf044ba575464f17f1f948d78ec92ad3a6922a7bfdf3ad6b6b2cad050",
|
"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.bz2",
|
"filename": "clang.tar.bz2",
|
||||||
"unpack": true
|
"unpack": true
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
"clang_version": "r247539"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 105219872,
|
"size": 106877168,
|
||||||
"digest": "aa8de2fa535d0667e079019c475c631ea008f1bb5228505510867255b4d9c30663e2c97e579220a575a5887aa3bcf250021b50f76b90c2fa8c65a7aa19270066",
|
"digest": "1c50c6348eaf429ed59bb603cff63bcc1f870f59216dd3c234db5b1156cfd351d5ee7b820ec31be4d2661eb4213b2e0030e2ba2782b42905d1ec19c7f8bd322a",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.xz",
|
"filename": "clang.tar.xz",
|
||||||
"unpack": true
|
"unpack": true
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
"clang_version": "r183744"
|
"clang_version": "r247539"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"size": 59602619,
|
"size": 97314461,
|
||||||
"digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
|
"digest": "9a74670fa917f760a4767923485d5166bbd258a8023c8aeb899b8c4d22f2847be76508ac5f26d7d2193318a2bb368a71bc62888d1bfe9d81eb45329a60451aa4",
|
||||||
"algorithm": "sha512",
|
"algorithm": "sha512",
|
||||||
"filename": "clang.tar.bz2",
|
"filename": "clang.tar.xz",
|
||||||
"unpack": true
|
"unpack": true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -189,6 +189,36 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
|||||||
<!ENTITY printButton.label "Print">
|
<!ENTITY printButton.label "Print">
|
||||||
<!ENTITY printButton.tooltip "Print this page">
|
<!ENTITY printButton.tooltip "Print this page">
|
||||||
|
|
||||||
|
<!ENTITY urlbar.viewSiteInfo.label "View site information">
|
||||||
|
<!-- LOCALIZATION NOTE: all of the following urlbar NotificationAnchor.label strings are
|
||||||
|
used to provide accessible labels to users of assistive technology like screenreaders.
|
||||||
|
It is not possible to see them visually in the UI. -->
|
||||||
|
<!ENTITY urlbar.defaultNotificationAnchor.label "View a notification">
|
||||||
|
<!ENTITY urlbar.geolocationNotificationAnchor.label "View the location request">
|
||||||
|
<!ENTITY urlbar.addonsNotificationAnchor.label "View the add-on install message">
|
||||||
|
<!ENTITY urlbar.indexedDBNotificationAnchor.label "View the app-offline storage message">
|
||||||
|
<!ENTITY urlbar.loginFillNotificationAnchor.label "Manage your login information">
|
||||||
|
<!ENTITY urlbar.passwordNotificationAnchor.label "Check if you want to save your password">
|
||||||
|
<!ENTITY urlbar.webappsNotificationAnchor.label "View the app install message">
|
||||||
|
<!ENTITY urlbar.pluginsNotificationAnchor.label "Manage plugin usage on this page">
|
||||||
|
<!ENTITY urlbar.webNotsNotificationAnchor.label "Change whether the site can show you notifications">
|
||||||
|
|
||||||
|
<!ENTITY urlbar.webRTCShareDevicesNotificationAnchor.label "Manage sharing your camera and/or microphone with the site">
|
||||||
|
<!ENTITY urlbar.webRTCSharingDevicesNotificationAnchor.label "You are sharing your camera and/or microphone with the site">
|
||||||
|
<!ENTITY urlbar.webRTCShareMicrophoneNotificationAnchor.label "Manage sharing your microphone with the site">
|
||||||
|
<!ENTITY urlbar.webRTCSharingMicrophoneNotificationAnchor.label "You are sharing your microphone with the site">
|
||||||
|
<!ENTITY urlbar.webRTCShareScreenNotificationAnchor.label "Manage sharing your windows or screen with the site">
|
||||||
|
<!ENTITY urlbar.webRTCSharingScreenNotificationAnchor.label "You are sharing a window or your screen with the site">
|
||||||
|
|
||||||
|
<!ENTITY urlbar.pointerLockNotificationAnchor.label "Change whether the site can hide the pointer">
|
||||||
|
<!ENTITY urlbar.servicesNotificationAnchor.label "View the service install message">
|
||||||
|
<!ENTITY urlbar.translateNotificationAnchor.label "Translate this page">
|
||||||
|
<!ENTITY urlbar.translatedNotificationAnchor.label "Manage page translation">
|
||||||
|
<!ENTITY urlbar.emeNotificationAnchor.label "Manage use of DRM software">
|
||||||
|
|
||||||
|
<!ENTITY urlbar.toggleAutocomplete.label "Toggle the autocomplete popup">
|
||||||
|
|
||||||
|
|
||||||
<!ENTITY locationItem.title "Location">
|
<!ENTITY locationItem.title "Location">
|
||||||
<!ENTITY searchItem.title "Search">
|
<!ENTITY searchItem.title "Search">
|
||||||
|
|
||||||
|
@ -13,8 +13,20 @@
|
|||||||
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
|
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
|
||||||
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
|
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
|
||||||
"patches": {
|
"patches": {
|
||||||
"macosx64": ["llvm-debug-frame.patch"],
|
"macosx64": [
|
||||||
"linux64": ["llvm-debug-frame.patch"],
|
"llvm-debug-frame.patch",
|
||||||
"linux32": ["llvm-debug-frame.patch"]
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux64": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux32": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,20 @@
|
|||||||
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
|
"cc": "/home/worker/workspace/build/src/gcc/bin/gcc",
|
||||||
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
|
"cxx": "/home/worker/workspace/build/src/gcc/bin/g++",
|
||||||
"patches": {
|
"patches": {
|
||||||
"macosx64": ["llvm-debug-frame.patch"],
|
"macosx64": [
|
||||||
"linux64": ["llvm-debug-frame.patch"],
|
"llvm-debug-frame.patch",
|
||||||
"linux32": ["llvm-debug-frame.patch"]
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux64": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux32": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,20 @@
|
|||||||
"cc": "/usr/bin/clang",
|
"cc": "/usr/bin/clang",
|
||||||
"cxx": "/usr/bin/clang++",
|
"cxx": "/usr/bin/clang++",
|
||||||
"patches": {
|
"patches": {
|
||||||
"macosx64": ["llvm-debug-frame.patch"],
|
"macosx64": [
|
||||||
"linux64": ["llvm-debug-frame.patch"],
|
"llvm-debug-frame.patch",
|
||||||
"linux32": ["llvm-debug-frame.patch"]
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux64": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
],
|
||||||
|
"linux32": [
|
||||||
|
"llvm-debug-frame.patch",
|
||||||
|
"query-selector-visibility.patch",
|
||||||
|
"return-empty-string-non-mangled.patch"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
build/unix/build-clang/query-selector-visibility.patch
Normal file
79
build/unix/build-clang/query-selector-visibility.patch
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
commit 865b9340996f9f9d04b73b187248737dc6fd845e
|
||||||
|
Author: Michael Wu <mwu@mozilla.com>
|
||||||
|
Date: Mon Sep 14 17:47:21 2015 -0400
|
||||||
|
|
||||||
|
Add support for querying the visibility of a cursor
|
||||||
|
|
||||||
|
diff --git a/llvm/tools/clang/include/clang-c/Index.h b/llvm/tools/clang/include/clang-c/Index.h
|
||||||
|
index fad9cfa..311bfcb 100644
|
||||||
|
--- a/llvm/tools/clang/include/clang-c/Index.h
|
||||||
|
+++ b/llvm/tools/clang/include/clang-c/Index.h
|
||||||
|
@@ -2440,6 +2440,24 @@ enum CXLinkageKind {
|
||||||
|
CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor);
|
||||||
|
|
||||||
|
/**
|
||||||
|
+ * \brief Describe the visibility of the entity referred to by a cursor.
|
||||||
|
+ */
|
||||||
|
+enum CXVisibilityKind {
|
||||||
|
+ /** \brief This value indicates that no visibility information is available
|
||||||
|
+ * for a provided CXCursor. */
|
||||||
|
+ CXVisibility_Invalid,
|
||||||
|
+
|
||||||
|
+ /** \brief Symbol not seen by the linker. */
|
||||||
|
+ CXVisibility_Hidden,
|
||||||
|
+ /** \brief Symbol seen by the linker but resolves to a symbol inside this object. */
|
||||||
|
+ CXVisibility_Protected,
|
||||||
|
+ /** \brief Symbol seen by the linker and acts like a normal symbol. */
|
||||||
|
+ CXVisibility_Default,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+CINDEX_LINKAGE enum CXVisibilityKind clang_getCursorVisibility(CXCursor cursor);
|
||||||
|
+
|
||||||
|
+/**
|
||||||
|
* \brief Determine the availability of the entity that this cursor refers to,
|
||||||
|
* taking the current target platform into account.
|
||||||
|
*
|
||||||
|
diff --git a/llvm/tools/clang/tools/libclang/CIndex.cpp b/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
index 8225a6c..9fa18d3 100644
|
||||||
|
--- a/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
+++ b/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
@@ -6361,6 +6361,27 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) {
|
||||||
|
} // end: extern "C"
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
+// Operations for querying visibility of a cursor.
|
||||||
|
+//===----------------------------------------------------------------------===//
|
||||||
|
+
|
||||||
|
+extern "C" {
|
||||||
|
+CXVisibilityKind clang_getCursorVisibility(CXCursor cursor) {
|
||||||
|
+ if (!clang_isDeclaration(cursor.kind))
|
||||||
|
+ return CXVisibility_Invalid;
|
||||||
|
+
|
||||||
|
+ const Decl *D = cxcursor::getCursorDecl(cursor);
|
||||||
|
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D))
|
||||||
|
+ switch (ND->getVisibility()) {
|
||||||
|
+ case HiddenVisibility: return CXVisibility_Hidden;
|
||||||
|
+ case ProtectedVisibility: return CXVisibility_Protected;
|
||||||
|
+ case DefaultVisibility: return CXVisibility_Default;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ return CXVisibility_Invalid;
|
||||||
|
+}
|
||||||
|
+} // end: extern "C"
|
||||||
|
+
|
||||||
|
+//===----------------------------------------------------------------------===//
|
||||||
|
// Operations for querying language of a cursor.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
diff --git a/llvm/tools/clang/tools/libclang/libclang.exports b/llvm/tools/clang/tools/libclang/libclang.exports
|
||||||
|
index f6a7175..a919a8e 100644
|
||||||
|
--- a/llvm/tools/clang/tools/libclang/libclang.exports
|
||||||
|
+++ b/llvm/tools/clang/tools/libclang/libclang.exports
|
||||||
|
@@ -173,6 +173,7 @@ clang_getCursorSemanticParent
|
||||||
|
clang_getCursorSpelling
|
||||||
|
clang_getCursorType
|
||||||
|
clang_getCursorUSR
|
||||||
|
+clang_getCursorVisibility
|
||||||
|
clang_getDeclObjCTypeEncoding
|
||||||
|
clang_getDefinitionSpellingAndExtent
|
||||||
|
clang_getDiagnostic
|
21
build/unix/build-clang/return-empty-string-non-mangled.patch
Normal file
21
build/unix/build-clang/return-empty-string-non-mangled.patch
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
commit 009de5ea7a1913f0b4619cf514787bd52af38c28
|
||||||
|
Author: Michael Wu <mwu@mozilla.com>
|
||||||
|
Date: Thu Sep 24 11:36:08 2015 -0400
|
||||||
|
|
||||||
|
Return an empty string when a symbol isn't mangled
|
||||||
|
|
||||||
|
diff --git a/llvm/tools/clang/tools/libclang/CIndex.cpp b/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
index 9fa18d3..1253832 100644
|
||||||
|
--- a/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
+++ b/llvm/tools/clang/tools/libclang/CIndex.cpp
|
||||||
|
@@ -3891,6 +3891,10 @@ CXString clang_Cursor_getMangling(CXCursor C) {
|
||||||
|
ASTContext &Ctx = ND->getASTContext();
|
||||||
|
std::unique_ptr<MangleContext> MC(Ctx.createMangleContext());
|
||||||
|
|
||||||
|
+ // Don't mangle if we don't need to.
|
||||||
|
+ if (!MC->shouldMangleCXXName(ND))
|
||||||
|
+ return cxstring::createEmpty();
|
||||||
|
+
|
||||||
|
std::string FrontendBuf;
|
||||||
|
llvm::raw_string_ostream FrontendBufOS(FrontendBuf);
|
||||||
|
MC->mangleName(ND, FrontendBufOS);
|
@ -7,13 +7,14 @@ import subprocess
|
|||||||
|
|
||||||
def get_all_toplevel_filenames():
|
def get_all_toplevel_filenames():
|
||||||
'''Get a list of all the files in the (Mercurial or Git) repository.'''
|
'''Get a list of all the files in the (Mercurial or Git) repository.'''
|
||||||
|
failed_cmds = []
|
||||||
try:
|
try:
|
||||||
cmd = ['hg', 'manifest', '-q']
|
cmd = ['hg', 'manifest', '-q']
|
||||||
all_filenames = subprocess.check_output(cmd, universal_newlines=True,
|
all_filenames = subprocess.check_output(cmd, universal_newlines=True,
|
||||||
stderr=subprocess.PIPE).split('\n')
|
stderr=subprocess.PIPE).split('\n')
|
||||||
return all_filenames
|
return all_filenames
|
||||||
except:
|
except:
|
||||||
pass
|
failed_cmds.append(cmd)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Get the relative path to the top-level directory.
|
# Get the relative path to the top-level directory.
|
||||||
@ -25,6 +26,6 @@ def get_all_toplevel_filenames():
|
|||||||
stderr=subprocess.PIPE).split('\n')
|
stderr=subprocess.PIPE).split('\n')
|
||||||
return all_filenames
|
return all_filenames
|
||||||
except:
|
except:
|
||||||
pass
|
failed_cmds.append(cmd)
|
||||||
|
|
||||||
raise Exception('failed to run any of the repo manifest commands', cmds)
|
raise Exception('failed to run any of the repo manifest commands', failed_cmds)
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||||
const { thunk } = require("./middleware/thunk");
|
const { thunk } = require("./middleware/thunk");
|
||||||
const { waitUntilService } = require("./middleware/wait-service");
|
const { waitUntilService } = require("./middleware/wait-service");
|
||||||
|
const { task } = require("./middleware/task");
|
||||||
const { log } = require("./middleware/log");
|
const { log } = require("./middleware/log");
|
||||||
const { promise } = require("./middleware/promise");
|
const { promise } = require("./middleware/promise");
|
||||||
|
|
||||||
@ -20,6 +21,7 @@ const { promise } = require("./middleware/promise");
|
|||||||
*/
|
*/
|
||||||
module.exports = (opts={}) => {
|
module.exports = (opts={}) => {
|
||||||
const middleware = [
|
const middleware = [
|
||||||
|
task,
|
||||||
thunk,
|
thunk,
|
||||||
waitUntilService,
|
waitUntilService,
|
||||||
promise,
|
promise,
|
||||||
|
@ -7,6 +7,9 @@
|
|||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'log.js',
|
'log.js',
|
||||||
'promise.js',
|
'promise.js',
|
||||||
|
'task.js',
|
||||||
'thunk.js',
|
'thunk.js',
|
||||||
'wait-service.js',
|
'wait-service.js',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell.ini']
|
||||||
|
42
devtools/client/shared/redux/middleware/task.js
Normal file
42
devtools/client/shared/redux/middleware/task.js
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { Task } = require("resource://gre/modules/Task.jsm");
|
||||||
|
const { executeSoon, isGenerator, isPromise, reportException } = require("devtools/shared/DevToolsUtils");
|
||||||
|
const ERROR_TYPE = exports.ERROR_TYPE = "@@redux/middleware/task#error";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A middleware that allows generator thunks (functions) and promise
|
||||||
|
* to be dispatched. If it's a generator, it is called with `dispatch` and `getState`,
|
||||||
|
* allowing the action to create multiple actions (most likely
|
||||||
|
* asynchronously) and yield on each. If called with a promise, calls `dispatch`
|
||||||
|
* on the results.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function task ({ dispatch, getState }) {
|
||||||
|
return next => action => {
|
||||||
|
if (isGenerator(action)) {
|
||||||
|
return Task.spawn(action.bind(null, dispatch, getState))
|
||||||
|
.then(null, handleError.bind(null, dispatch));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (isPromise(action)) {
|
||||||
|
return action.then(dispatch, handleError.bind(null, dispatch));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError (dispatch, error) {
|
||||||
|
executeSoon(() => {
|
||||||
|
reportException(ERROR_TYPE, error);
|
||||||
|
dispatch({ type: ERROR_TYPE, error });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.task = task;
|
27
devtools/client/shared/redux/middleware/test/head.js
Normal file
27
devtools/client/shared/redux/middleware/test/head.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||||
|
var { require } = Cu.import("resource://gre/modules/devtools/shared/Loader.jsm", {});
|
||||||
|
var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||||
|
var promise = require("promise");
|
||||||
|
|
||||||
|
DevToolsUtils.testing = true;
|
||||||
|
|
||||||
|
function waitUntilState (store, predicate) {
|
||||||
|
let deferred = promise.defer();
|
||||||
|
let unsubscribe = store.subscribe(check);
|
||||||
|
|
||||||
|
function check () {
|
||||||
|
if (predicate(store.getState())) {
|
||||||
|
unsubscribe();
|
||||||
|
deferred.resolve()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire the check immediately incase the action has already occurred
|
||||||
|
check();
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||||
|
const { task } = require("devtools/client/shared/redux/middleware/task");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that task middleware allows dispatching generators, promises and objects
|
||||||
|
* that return actions;
|
||||||
|
*/
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function *() {
|
||||||
|
let store = applyMiddleware(task)(createStore)(reducer);
|
||||||
|
|
||||||
|
store.dispatch(fetch1("generator"));
|
||||||
|
yield waitUntilState(store, () => store.getState().length === 1);
|
||||||
|
equal(store.getState()[0].data, "generator", "task middleware async dispatches an action via generator");
|
||||||
|
|
||||||
|
store.dispatch(fetch2("sync"));
|
||||||
|
yield waitUntilState(store, () => store.getState().length === 2);
|
||||||
|
equal(store.getState()[1].data, "sync", "task middleware sync dispatches an action via sync");
|
||||||
|
});
|
||||||
|
|
||||||
|
function fetch1 (data) {
|
||||||
|
return function *(dispatch, getState) {
|
||||||
|
equal(getState().length, 0, "`getState` is accessible in a generator action");
|
||||||
|
let moreData = yield new Promise(resolve => resolve(data));
|
||||||
|
// Ensure it handles more than one yield
|
||||||
|
moreData = yield new Promise(resolve => resolve(data));
|
||||||
|
dispatch({ type: "fetch1", data: moreData });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetch2 (data) {
|
||||||
|
return {
|
||||||
|
type: "fetch2",
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function reducer (state=[], action) {
|
||||||
|
do_print("Action called: " + action.type);
|
||||||
|
if (["fetch1", "fetch2"].includes(action.type)) {
|
||||||
|
state.push(action);
|
||||||
|
}
|
||||||
|
return [...state];
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that task middleware allows dispatching generators that dispatch
|
||||||
|
* additional sync and async actions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||||
|
const { task } = require("devtools/client/shared/redux/middleware/task");
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function *() {
|
||||||
|
let store = applyMiddleware(task)(createStore)(reducer);
|
||||||
|
|
||||||
|
store.dispatch(comboAction());
|
||||||
|
yield waitUntilState(store, () => store.getState().length === 3);
|
||||||
|
|
||||||
|
equal(store.getState()[0].type, "fetchAsync-start", "Async dispatched actions in a generator task are fired");
|
||||||
|
equal(store.getState()[1].type, "fetchAsync-end", "Async dispatched actions in a generator task are fired");
|
||||||
|
equal(store.getState()[2].type, "fetchSync", "Return values of yielded sync dispatched actions are correct");
|
||||||
|
equal(store.getState()[3].type, "fetch-done", "Return values of yielded async dispatched actions are correct");
|
||||||
|
equal(store.getState()[3].data.sync.data, "sync", "Return values of dispatched sync values are correct");
|
||||||
|
equal(store.getState()[3].data.async, "async", "Return values of dispatched async values are correct");
|
||||||
|
});
|
||||||
|
|
||||||
|
function comboAction () {
|
||||||
|
return function *(dispatch, getState) {
|
||||||
|
let data = {};
|
||||||
|
data.async = yield dispatch(fetchAsync("async"));
|
||||||
|
data.sync = yield dispatch(fetchSync("sync"));
|
||||||
|
dispatch({ type: "fetch-done", data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchSync (data) {
|
||||||
|
return { type: "fetchSync", data };
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchAsync (data) {
|
||||||
|
return function *(dispatch) {
|
||||||
|
dispatch({ type: "fetchAsync-start" });
|
||||||
|
let val = yield new Promise(resolve => resolve(data));
|
||||||
|
dispatch({ type: "fetchAsync-end" });
|
||||||
|
return val;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function reducer (state=[], action) {
|
||||||
|
do_print("Action called: " + action.type);
|
||||||
|
if (/fetch/.test(action.type)) {
|
||||||
|
state.push(action);
|
||||||
|
}
|
||||||
|
return [...state];
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
const { createStore, applyMiddleware } = require("devtools/client/shared/vendor/redux");
|
||||||
|
const { task, ERROR_TYPE } = require("devtools/client/shared/redux/middleware/task");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that the middleware handles errors thrown in tasks, and rejected promises.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function *() {
|
||||||
|
let store = applyMiddleware(task)(createStore)(reducer);
|
||||||
|
|
||||||
|
store.dispatch(generatorError());
|
||||||
|
yield waitUntilState(store, () => store.getState().length === 1);
|
||||||
|
equal(store.getState()[0].type, ERROR_TYPE, "generator errors dispatch ERROR_TYPE actions");
|
||||||
|
equal(store.getState()[0].error, "task-middleware-error-generator", "generator errors dispatch ERROR_TYPE actions with error");
|
||||||
|
});
|
||||||
|
|
||||||
|
function generatorError () {
|
||||||
|
return function *(dispatch, getState) {
|
||||||
|
throw "task-middleware-error-generator";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function reducer (state=[], action) {
|
||||||
|
do_print("Action called: " + action.type);
|
||||||
|
if (action.type === ERROR_TYPE) {
|
||||||
|
state.push(action);
|
||||||
|
}
|
||||||
|
return [...state];
|
||||||
|
}
|
10
devtools/client/shared/redux/middleware/test/xpcshell.ini
Normal file
10
devtools/client/shared/redux/middleware/test/xpcshell.ini
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[DEFAULT]
|
||||||
|
tags = devtools
|
||||||
|
head = head.js
|
||||||
|
tail =
|
||||||
|
firefox-appdir = browser
|
||||||
|
skip-if = toolkit == 'android' || toolkit == 'gonk'
|
||||||
|
|
||||||
|
[test_middleware-task-01.js]
|
||||||
|
[test_middleware-task-02.js]
|
||||||
|
[test_middleware-task-03.js]
|
@ -1,4 +1,4 @@
|
|||||||
@namespace url("images/http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||||
|
|
||||||
scrollbar {
|
scrollbar {
|
||||||
-moz-appearance: none !important;
|
-moz-appearance: none !important;
|
||||||
|
@ -755,3 +755,11 @@ exports.openFileStream = function (filePath) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.isGenerator = function (fn) {
|
||||||
|
return typeof fn === "function" && fn.isGenerator();
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.isPromise = function (p) {
|
||||||
|
return p && typeof p.then === "function";
|
||||||
|
};
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "mozilla/dom/WindowBinding.h"
|
#include "mozilla/dom/WindowBinding.h"
|
||||||
#include "mozilla/dom/ElementBinding.h"
|
#include "mozilla/dom/ElementBinding.h"
|
||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
|
#include "nsContentListDeclarations.h"
|
||||||
|
|
||||||
class nsIFrame;
|
class nsIFrame;
|
||||||
class nsIDOMMozNamedAttrMap;
|
class nsIDOMMozNamedAttrMap;
|
||||||
@ -61,11 +62,6 @@ namespace dom {
|
|||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
||||||
already_AddRefed<nsContentList>
|
|
||||||
NS_GetContentList(nsINode* aRootNode,
|
|
||||||
int32_t aMatchNameSpaceId,
|
|
||||||
const nsAString& aTagname);
|
|
||||||
|
|
||||||
#define ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_))
|
#define ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_))
|
||||||
|
|
||||||
// Element-specific flags
|
// Element-specific flags
|
||||||
|
@ -198,7 +198,8 @@ NS_GetContentList(nsINode* aRootNode,
|
|||||||
NS_ASSERTION(aRootNode, "content list has to have a root");
|
NS_ASSERTION(aRootNode, "content list has to have a root");
|
||||||
|
|
||||||
nsRefPtr<nsContentList> list;
|
nsRefPtr<nsContentList> list;
|
||||||
nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname);
|
nsContentListKey hashKey(aRootNode, aMatchNameSpaceId, aTagname,
|
||||||
|
aRootNode->OwnerDoc()->IsHTMLDocument());
|
||||||
uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(hashKey);
|
uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(hashKey);
|
||||||
nsContentList* cachedList = sRecentlyUsedContentLists[recentlyUsedCacheIndex];
|
nsContentList* cachedList = sRecentlyUsedContentLists[recentlyUsedCacheIndex];
|
||||||
if (cachedList && cachedList->MatchesKey(hashKey)) {
|
if (cachedList && cachedList->MatchesKey(hashKey)) {
|
||||||
@ -398,7 +399,8 @@ nsContentList::nsContentList(nsINode* aRootNode,
|
|||||||
mData(nullptr),
|
mData(nullptr),
|
||||||
mState(LIST_DIRTY),
|
mState(LIST_DIRTY),
|
||||||
mDeep(aDeep),
|
mDeep(aDeep),
|
||||||
mFuncMayDependOnAttr(false)
|
mFuncMayDependOnAttr(false),
|
||||||
|
mIsHTMLDocument(aRootNode->OwnerDoc()->IsHTMLDocument())
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mRootNode, "Must have root");
|
NS_ASSERTION(mRootNode, "Must have root");
|
||||||
if (nsGkAtoms::_asterisk == mHTMLMatchAtom) {
|
if (nsGkAtoms::_asterisk == mHTMLMatchAtom) {
|
||||||
@ -438,7 +440,8 @@ nsContentList::nsContentList(nsINode* aRootNode,
|
|||||||
mState(LIST_DIRTY),
|
mState(LIST_DIRTY),
|
||||||
mMatchAll(false),
|
mMatchAll(false),
|
||||||
mDeep(aDeep),
|
mDeep(aDeep),
|
||||||
mFuncMayDependOnAttr(aFuncMayDependOnAttr)
|
mFuncMayDependOnAttr(aFuncMayDependOnAttr),
|
||||||
|
mIsHTMLDocument(false)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mRootNode, "Must have root");
|
NS_ASSERTION(mRootNode, "Must have root");
|
||||||
mRootNode->AddMutationObserver(this);
|
mRootNode->AddMutationObserver(this);
|
||||||
@ -839,25 +842,20 @@ nsContentList::Match(Element *aElement)
|
|||||||
if (!mXMLMatchAtom)
|
if (!mXMLMatchAtom)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mozilla::dom::NodeInfo *ni = aElement->NodeInfo();
|
NodeInfo *ni = aElement->NodeInfo();
|
||||||
|
|
||||||
bool unknown = mMatchNameSpaceId == kNameSpaceID_Unknown;
|
bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard ||
|
||||||
bool wildcard = mMatchNameSpaceId == kNameSpaceID_Wildcard;
|
mMatchNameSpaceId == kNameSpaceID_Unknown;
|
||||||
bool toReturn = mMatchAll;
|
bool toReturn = mMatchAll;
|
||||||
if (!unknown && !wildcard)
|
if (!wildcard)
|
||||||
toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
|
toReturn &= ni->NamespaceEquals(mMatchNameSpaceId);
|
||||||
|
|
||||||
if (toReturn)
|
if (toReturn)
|
||||||
return toReturn;
|
return toReturn;
|
||||||
|
|
||||||
bool matchHTML = aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
|
bool matchHTML =
|
||||||
aElement->OwnerDoc()->IsHTMLDocument();
|
mIsHTMLDocument && aElement->GetNameSpaceID() == kNameSpaceID_XHTML;
|
||||||
|
|
||||||
if (unknown) {
|
|
||||||
return matchHTML ? ni->QualifiedNameEquals(mHTMLMatchAtom) :
|
|
||||||
ni->QualifiedNameEquals(mXMLMatchAtom);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wildcard) {
|
if (wildcard) {
|
||||||
return matchHTML ? ni->Equals(mHTMLMatchAtom) :
|
return matchHTML ? ni->Equals(mHTMLMatchAtom) :
|
||||||
ni->Equals(mXMLMatchAtom);
|
ni->Equals(mXMLMatchAtom);
|
||||||
@ -962,7 +960,7 @@ nsContentList::RemoveFromHashtable()
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsDependentAtomString str(mXMLMatchAtom);
|
nsDependentAtomString str(mXMLMatchAtom);
|
||||||
nsContentListKey key(mRootNode, mMatchNameSpaceId, str);
|
nsContentListKey key(mRootNode, mMatchNameSpaceId, str, mIsHTMLDocument);
|
||||||
uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
|
uint32_t recentlyUsedCacheIndex = RecentlyUsedCacheIndex(key);
|
||||||
if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
|
if (sRecentlyUsedContentLists[recentlyUsedCacheIndex] == this) {
|
||||||
sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
|
sRecentlyUsedContentLists[recentlyUsedCacheIndex] = nullptr;
|
||||||
|
@ -142,14 +142,21 @@ private:
|
|||||||
*/
|
*/
|
||||||
struct nsContentListKey
|
struct nsContentListKey
|
||||||
{
|
{
|
||||||
|
// We have to take an aIsHTMLDocument arg for two reasons:
|
||||||
|
// 1) We don't want to include nsIDocument.h in this header.
|
||||||
|
// 2) We need to do that to make nsContentList::RemoveFromHashtable
|
||||||
|
// work, because by the time it's called the document of the
|
||||||
|
// list's root node might have changed.
|
||||||
nsContentListKey(nsINode* aRootNode,
|
nsContentListKey(nsINode* aRootNode,
|
||||||
int32_t aMatchNameSpaceId,
|
int32_t aMatchNameSpaceId,
|
||||||
const nsAString& aTagname)
|
const nsAString& aTagname,
|
||||||
|
bool aIsHTMLDocument)
|
||||||
: mRootNode(aRootNode),
|
: mRootNode(aRootNode),
|
||||||
mMatchNameSpaceId(aMatchNameSpaceId),
|
mMatchNameSpaceId(aMatchNameSpaceId),
|
||||||
mTagname(aTagname),
|
mTagname(aTagname),
|
||||||
|
mIsHTMLDocument(aIsHTMLDocument),
|
||||||
mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode,
|
mHash(mozilla::AddToHash(mozilla::HashString(aTagname), mRootNode,
|
||||||
mMatchNameSpaceId))
|
mMatchNameSpaceId, mIsHTMLDocument))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +164,7 @@ struct nsContentListKey
|
|||||||
: mRootNode(aContentListKey.mRootNode),
|
: mRootNode(aContentListKey.mRootNode),
|
||||||
mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId),
|
mMatchNameSpaceId(aContentListKey.mMatchNameSpaceId),
|
||||||
mTagname(aContentListKey.mTagname),
|
mTagname(aContentListKey.mTagname),
|
||||||
|
mIsHTMLDocument(aContentListKey.mIsHTMLDocument),
|
||||||
mHash(aContentListKey.mHash)
|
mHash(aContentListKey.mHash)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -169,6 +177,7 @@ struct nsContentListKey
|
|||||||
nsINode* const mRootNode; // Weak ref
|
nsINode* const mRootNode; // Weak ref
|
||||||
const int32_t mMatchNameSpaceId;
|
const int32_t mMatchNameSpaceId;
|
||||||
const nsAString& mTagname;
|
const nsAString& mTagname;
|
||||||
|
bool mIsHTMLDocument;
|
||||||
const uint32_t mHash;
|
const uint32_t mHash;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -209,7 +218,7 @@ public:
|
|||||||
* The special value "*" always matches whatever aMatchAtom
|
* The special value "*" always matches whatever aMatchAtom
|
||||||
* is matched against.
|
* is matched against.
|
||||||
* @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
|
* @param aMatchNameSpaceId If kNameSpaceID_Unknown, then aMatchAtom is the
|
||||||
* tagName to match.
|
* localName to match.
|
||||||
* If kNameSpaceID_Wildcard, then aMatchAtom is the
|
* If kNameSpaceID_Wildcard, then aMatchAtom is the
|
||||||
* localName to match.
|
* localName to match.
|
||||||
* Otherwise we match nodes whose namespace is
|
* Otherwise we match nodes whose namespace is
|
||||||
@ -237,7 +246,8 @@ public:
|
|||||||
* deeper. If true, then look at the whole subtree rooted at
|
* deeper. If true, then look at the whole subtree rooted at
|
||||||
* our root.
|
* our root.
|
||||||
* @param aMatchAtom an atom to be passed back to aFunc
|
* @param aMatchAtom an atom to be passed back to aFunc
|
||||||
* @param aMatchNameSpaceId a namespace id to be passed back to aFunc
|
* @param aMatchNameSpaceId a namespace id to be passed back to aFunc. Is
|
||||||
|
allowed to be kNameSpaceID_Unknown.
|
||||||
* @param aFuncMayDependOnAttr a boolean that indicates whether this list is
|
* @param aFuncMayDependOnAttr a boolean that indicates whether this list is
|
||||||
* sensitive to attribute changes.
|
* sensitive to attribute changes.
|
||||||
*/
|
*/
|
||||||
@ -318,13 +328,15 @@ public:
|
|||||||
{
|
{
|
||||||
// The root node is most commonly the same: the document. And the
|
// The root node is most commonly the same: the document. And the
|
||||||
// most common namespace id is kNameSpaceID_Unknown. So check the
|
// most common namespace id is kNameSpaceID_Unknown. So check the
|
||||||
// string first.
|
// string first. Cases in which whether our root's ownerDocument
|
||||||
|
// is HTML changes are extremely rare, so check those last.
|
||||||
NS_PRECONDITION(mXMLMatchAtom,
|
NS_PRECONDITION(mXMLMatchAtom,
|
||||||
"How did we get here with a null match atom on our list?");
|
"How did we get here with a null match atom on our list?");
|
||||||
return
|
return
|
||||||
mXMLMatchAtom->Equals(aKey.mTagname) &&
|
mXMLMatchAtom->Equals(aKey.mTagname) &&
|
||||||
mRootNode == aKey.mRootNode &&
|
mRootNode == aKey.mRootNode &&
|
||||||
mMatchNameSpaceId == aKey.mMatchNameSpaceId;
|
mMatchNameSpaceId == aKey.mMatchNameSpaceId &&
|
||||||
|
mIsHTMLDocument == aKey.mIsHTMLDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -445,6 +457,12 @@ protected:
|
|||||||
* Whether we actually need to flush to get our state correct.
|
* Whether we actually need to flush to get our state correct.
|
||||||
*/
|
*/
|
||||||
uint8_t mFlushesNeeded : 1;
|
uint8_t mFlushesNeeded : 1;
|
||||||
|
/**
|
||||||
|
* Whether the ownerDocument of our root node at list creation time was an
|
||||||
|
* HTML document. Only needed when we're doing a namespace/atom match, not
|
||||||
|
* when doing function matching, always false otherwise.
|
||||||
|
*/
|
||||||
|
uint8_t mIsHTMLDocument : 1;
|
||||||
|
|
||||||
#ifdef DEBUG_CONTENT_LIST
|
#ifdef DEBUG_CONTENT_LIST
|
||||||
void AssertInSync();
|
void AssertInSync();
|
||||||
|
@ -9,12 +9,14 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nsStringFwd.h"
|
|
||||||
|
|
||||||
class nsContentList;
|
class nsContentList;
|
||||||
class nsIAtom;
|
class nsIAtom;
|
||||||
class nsIContent;
|
class nsIContent;
|
||||||
class nsINode;
|
class nsINode;
|
||||||
|
// Can't use nsStringFwd.h because that's internal-API-only.
|
||||||
|
class nsString;
|
||||||
|
class nsAString;
|
||||||
|
|
||||||
// Magic namespace id that means "match all namespaces". This is
|
// Magic namespace id that means "match all namespaces". This is
|
||||||
// negative so it won't collide with actual namespace constants.
|
// negative so it won't collide with actual namespace constants.
|
||||||
@ -42,8 +44,9 @@ typedef void* (*nsFuncStringContentListDataAllocator)(nsINode* aRootNode,
|
|||||||
// If aMatchNameSpaceId is kNameSpaceID_Unknown, this will return a
|
// If aMatchNameSpaceId is kNameSpaceID_Unknown, this will return a
|
||||||
// content list which matches ASCIIToLower(aTagname) against HTML
|
// content list which matches ASCIIToLower(aTagname) against HTML
|
||||||
// elements in HTML documents and aTagname against everything else.
|
// elements in HTML documents and aTagname against everything else.
|
||||||
// For any other value of aMatchNameSpaceId, the list will match
|
// The comparison is done to the element's localName. For any
|
||||||
// aTagname against all elements.
|
// other value of aMatchNameSpaceId, the list will match aTagname
|
||||||
|
// against all elements, again comparing to the localName.
|
||||||
already_AddRefed<nsContentList>
|
already_AddRefed<nsContentList>
|
||||||
NS_GetContentList(nsINode* aRootNode,
|
NS_GetContentList(nsINode* aRootNode,
|
||||||
int32_t aMatchNameSpaceId,
|
int32_t aMatchNameSpaceId,
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "mozilla/UseCounter.h"
|
#include "mozilla/UseCounter.h"
|
||||||
#include "mozilla/WeakPtr.h"
|
#include "mozilla/WeakPtr.h"
|
||||||
#include "Units.h"
|
#include "Units.h"
|
||||||
|
#include "nsContentListDeclarations.h"
|
||||||
#include "nsExpirationTracker.h"
|
#include "nsExpirationTracker.h"
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
#include "prclist.h"
|
#include "prclist.h"
|
||||||
@ -176,11 +177,6 @@ enum DocumentFlavor {
|
|||||||
// Some function forward-declarations
|
// Some function forward-declarations
|
||||||
class nsContentList;
|
class nsContentList;
|
||||||
|
|
||||||
already_AddRefed<nsContentList>
|
|
||||||
NS_GetContentList(nsINode* aRootNode,
|
|
||||||
int32_t aMatchNameSpaceId,
|
|
||||||
const nsAString& aTagname);
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// Document interface. This is implemented by all document objects in
|
// Document interface. This is implemented by all document objects in
|
||||||
|
@ -32,7 +32,7 @@ function test_getElementsByTagName()
|
|||||||
do_check_eq(doc.getElementById("test2").getElementsByTagName("*").length,
|
do_check_eq(doc.getElementById("test2").getElementsByTagName("*").length,
|
||||||
8);
|
8);
|
||||||
do_check_eq(doc.getElementById("test2").getElementsByTagName("test").length,
|
do_check_eq(doc.getElementById("test2").getElementsByTagName("test").length,
|
||||||
3);
|
7);
|
||||||
|
|
||||||
// Check that the first element of getElementsByTagName on the document is
|
// Check that the first element of getElementsByTagName on the document is
|
||||||
// the right thing.
|
// the right thing.
|
||||||
@ -40,7 +40,7 @@ function test_getElementsByTagName()
|
|||||||
|
|
||||||
// Check that we get the right things in the right order
|
// Check that we get the right things in the right order
|
||||||
var numTests = doc.getElementsByTagName("test").length;
|
var numTests = doc.getElementsByTagName("test").length;
|
||||||
do_check_eq(numTests, 5);
|
do_check_eq(numTests, 14);
|
||||||
|
|
||||||
for (var i = 1; i <= numTests; ++i) {
|
for (var i = 1; i <= numTests; ++i) {
|
||||||
do_check_true(doc.getElementById("test" + i) instanceof nsIDOMElement);
|
do_check_true(doc.getElementById("test" + i) instanceof nsIDOMElement);
|
||||||
@ -51,15 +51,15 @@ function test_getElementsByTagName()
|
|||||||
// Check that we handle tagnames containing ':' correctly
|
// Check that we handle tagnames containing ':' correctly
|
||||||
do_check_true(doc.getElementsByTagName("foo:test")
|
do_check_true(doc.getElementsByTagName("foo:test")
|
||||||
instanceof nsIDOMNodeList);
|
instanceof nsIDOMNodeList);
|
||||||
do_check_eq(doc.getElementsByTagName("foo:test").length, 2);
|
do_check_eq(doc.getElementsByTagName("foo:test").length, 0);
|
||||||
|
|
||||||
do_check_true(doc.getElementsByTagName("foo2:test")
|
do_check_true(doc.getElementsByTagName("foo2:test")
|
||||||
instanceof nsIDOMNodeList);
|
instanceof nsIDOMNodeList);
|
||||||
do_check_eq(doc.getElementsByTagName("foo2:test").length, 3);
|
do_check_eq(doc.getElementsByTagName("foo2:test").length, 0);
|
||||||
|
|
||||||
do_check_true(doc.getElementsByTagName("bar:test")
|
do_check_true(doc.getElementsByTagName("bar:test")
|
||||||
instanceof nsIDOMNodeList);
|
instanceof nsIDOMNodeList);
|
||||||
do_check_eq(doc.getElementsByTagName("bar:test").length, 4);
|
do_check_eq(doc.getElementsByTagName("bar:test").length, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function test_getElementsByTagNameNS()
|
function test_getElementsByTagNameNS()
|
||||||
|
@ -700,6 +700,10 @@ DOMInterfaces = {
|
|||||||
'notflattened': True
|
'notflattened': True
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'IterableIterator': {
|
||||||
|
'skipGen': True
|
||||||
|
},
|
||||||
|
|
||||||
'KeyEvent': {
|
'KeyEvent': {
|
||||||
'concrete': False
|
'concrete': False
|
||||||
},
|
},
|
||||||
|
@ -1121,7 +1121,10 @@ class CGHeaders(CGWrapper):
|
|||||||
# Now for non-callback descriptors make sure we include any
|
# Now for non-callback descriptors make sure we include any
|
||||||
# headers needed by Func declarations.
|
# headers needed by Func declarations.
|
||||||
for desc in descriptors:
|
for desc in descriptors:
|
||||||
if desc.interface.isExternal():
|
# If this is an iterator interface generated for a seperate
|
||||||
|
# iterable interface, skip generating type includes, as we have
|
||||||
|
# what we need in IterableIterator.h
|
||||||
|
if desc.interface.isExternal() or desc.interface.isIteratorInterface():
|
||||||
continue
|
continue
|
||||||
|
|
||||||
def addHeaderForFunc(func):
|
def addHeaderForFunc(func):
|
||||||
@ -1148,16 +1151,15 @@ class CGHeaders(CGWrapper):
|
|||||||
if funcList is not None:
|
if funcList is not None:
|
||||||
addHeaderForFunc(funcList[0])
|
addHeaderForFunc(funcList[0])
|
||||||
|
|
||||||
for desc in descriptors:
|
if desc.interface.maplikeOrSetlikeOrIterable:
|
||||||
if desc.interface.maplikeOrSetlike:
|
|
||||||
# We need ToJSValue.h for maplike/setlike type conversions
|
# We need ToJSValue.h for maplike/setlike type conversions
|
||||||
bindingHeaders.add("mozilla/dom/ToJSValue.h")
|
bindingHeaders.add("mozilla/dom/ToJSValue.h")
|
||||||
# Add headers for the key and value types of the maplike, since
|
# Add headers for the key and value types of the maplike, since
|
||||||
# they'll be needed for convenience functions
|
# they'll be needed for convenience functions
|
||||||
addHeadersForType((desc.interface.maplikeOrSetlike.keyType,
|
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||||
desc, None))
|
desc, None))
|
||||||
if desc.interface.maplikeOrSetlike.valueType:
|
if desc.interface.maplikeOrSetlikeOrIterable.valueType:
|
||||||
addHeadersForType((desc.interface.maplikeOrSetlike.valueType,
|
addHeadersForType((desc.interface.maplikeOrSetlikeOrIterable.valueType,
|
||||||
desc, None))
|
desc, None))
|
||||||
|
|
||||||
for d in dictionaries:
|
for d in dictionaries:
|
||||||
@ -2197,13 +2199,18 @@ class MethodDefiner(PropertyDefiner):
|
|||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Iterable methods should be enumerable, maplike/setlike methods
|
||||||
|
# should not.
|
||||||
|
isMaplikeOrSetlikeMethod = (m.isMaplikeOrSetlikeOrIterableMethod() and
|
||||||
|
(m.maplikeOrSetlikeOrIterable.isMaplike() or
|
||||||
|
m.maplikeOrSetlikeOrIterable.isSetlike()))
|
||||||
method = {
|
method = {
|
||||||
"name": m.identifier.name,
|
"name": m.identifier.name,
|
||||||
"methodInfo": not m.isStatic(),
|
"methodInfo": not m.isStatic(),
|
||||||
"length": methodLength(m),
|
"length": methodLength(m),
|
||||||
# Methods generated for a maplike/setlike declaration are not
|
# Methods generated for a maplike/setlike declaration are not
|
||||||
# enumerable.
|
# enumerable.
|
||||||
"flags": "JSPROP_ENUMERATE" if not m.isMaplikeOrSetlikeMethod() else "0",
|
"flags": "JSPROP_ENUMERATE" if not isMaplikeOrSetlikeMethod else "0",
|
||||||
"condition": PropertyDefiner.getControllingCondition(m, descriptor),
|
"condition": PropertyDefiner.getControllingCondition(m, descriptor),
|
||||||
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
|
"allowCrossOriginThis": m.getExtendedAttribute("CrossOriginCallable"),
|
||||||
"returnsPromise": m.returnsPromise(),
|
"returnsPromise": m.returnsPromise(),
|
||||||
@ -2247,19 +2254,23 @@ class MethodDefiner(PropertyDefiner):
|
|||||||
|
|
||||||
# Generate the maplike/setlike iterator, if one wasn't already
|
# Generate the maplike/setlike iterator, if one wasn't already
|
||||||
# generated by a method. If we already have an @@iterator symbol, fail.
|
# generated by a method. If we already have an @@iterator symbol, fail.
|
||||||
if descriptor.interface.maplikeOrSetlike:
|
if descriptor.interface.maplikeOrSetlikeOrIterable:
|
||||||
if hasIterator(methods, self.regular):
|
if hasIterator(methods, self.regular):
|
||||||
raise TypeError("Cannot have maplike/setlike interface with "
|
raise TypeError("Cannot have maplike/setlike/iterable interface with "
|
||||||
"other members that generate @@iterator "
|
"other members that generate @@iterator "
|
||||||
"on interface %s, such as indexed getters "
|
"on interface %s, such as indexed getters "
|
||||||
"or aliased functions." %
|
"or aliased functions." %
|
||||||
self.descriptor.interface.identifier.name)
|
self.descriptor.interface.identifier.name)
|
||||||
for m in methods:
|
for m in methods:
|
||||||
if (m.isMaplikeOrSetlikeMethod() and
|
if (m.isMaplikeOrSetlikeOrIterableMethod() and
|
||||||
((m.maplikeOrSetlike.isMaplike() and
|
(((m.maplikeOrSetlikeOrIterable.isMaplike() or
|
||||||
m.identifier.name == "entries") or
|
(m.maplikeOrSetlikeOrIterable.isIterable() and
|
||||||
(m.maplikeOrSetlike.isSetlike() and
|
m.maplikeOrSetlikeOrIterable.hasValueType())) and
|
||||||
m.identifier.name == "values"))):
|
m.identifier.name == "entries") or
|
||||||
|
(((m.maplikeOrSetlikeOrIterable.isSetlike() or
|
||||||
|
(m.maplikeOrSetlikeOrIterable.isIterable() and
|
||||||
|
not m.maplikeOrSetlikeOrIterable.hasValueType()))) and
|
||||||
|
m.identifier.name == "values"))):
|
||||||
self.regular.append({
|
self.regular.append({
|
||||||
"name": "@@iterator",
|
"name": "@@iterator",
|
||||||
"methodName": m.identifier.name,
|
"methodName": m.identifier.name,
|
||||||
@ -5810,8 +5821,10 @@ class CGArgumentConverter(CGThing):
|
|||||||
# If we have a method generated by the maplike/setlike portion of an
|
# If we have a method generated by the maplike/setlike portion of an
|
||||||
# interface, arguments can possibly be undefined, but will need to be
|
# interface, arguments can possibly be undefined, but will need to be
|
||||||
# converted to the key/value type of the backing object. In this case,
|
# converted to the key/value type of the backing object. In this case,
|
||||||
# use .get() instead of direct access to the argument.
|
# use .get() instead of direct access to the argument. This won't
|
||||||
if member.isMethod() and member.isMaplikeOrSetlikeMethod():
|
# matter for iterable since generated functions for those interface
|
||||||
|
# don't take arguments.
|
||||||
|
if member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod():
|
||||||
self.replacementVariables["val"] = string.Template(
|
self.replacementVariables["val"] = string.Template(
|
||||||
"args.get(${index})").substitute(replacer)
|
"args.get(${index})").substitute(replacer)
|
||||||
else:
|
else:
|
||||||
@ -7111,10 +7124,16 @@ class CGPerSignatureCall(CGThing):
|
|||||||
# If this is a method that was generated by a maplike/setlike
|
# If this is a method that was generated by a maplike/setlike
|
||||||
# interface, use the maplike/setlike generator to fill in the body.
|
# interface, use the maplike/setlike generator to fill in the body.
|
||||||
# Otherwise, use CGCallGenerator to call the native method.
|
# Otherwise, use CGCallGenerator to call the native method.
|
||||||
if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeMethod():
|
if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
|
||||||
cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
|
if (idlNode.maplikeOrSetlikeOrIterable.isMaplike() or
|
||||||
idlNode.maplikeOrSetlike,
|
idlNode.maplikeOrSetlikeOrIterable.isSetlike()):
|
||||||
idlNode.identifier.name))
|
cgThings.append(CGMaplikeOrSetlikeMethodGenerator(descriptor,
|
||||||
|
idlNode.maplikeOrSetlikeOrIterable,
|
||||||
|
idlNode.identifier.name))
|
||||||
|
else:
|
||||||
|
cgThings.append(CGIterableMethodGenerator(descriptor,
|
||||||
|
idlNode.maplikeOrSetlikeOrIterable,
|
||||||
|
idlNode.identifier.name))
|
||||||
else:
|
else:
|
||||||
cgThings.append(CGCallGenerator(
|
cgThings.append(CGCallGenerator(
|
||||||
self.getErrorReport() if self.isFallible() else None,
|
self.getErrorReport() if self.isFallible() else None,
|
||||||
@ -7349,7 +7368,7 @@ class CGMethodCall(CGThing):
|
|||||||
# Skip required arguments check for maplike/setlike interfaces, as
|
# Skip required arguments check for maplike/setlike interfaces, as
|
||||||
# they can have arguments which are not passed, and are treated as
|
# they can have arguments which are not passed, and are treated as
|
||||||
# if undefined had been explicitly passed.
|
# if undefined had been explicitly passed.
|
||||||
if requiredArgs > 0 and not method.isMaplikeOrSetlikeMethod():
|
if requiredArgs > 0 and not method.isMaplikeOrSetlikeOrIterableMethod():
|
||||||
code = fill(
|
code = fill(
|
||||||
"""
|
"""
|
||||||
if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
|
if (MOZ_UNLIKELY(args.length() < ${requiredArgs})) {
|
||||||
@ -12779,6 +12798,10 @@ class CGForwardDeclarations(CGWrapper):
|
|||||||
|
|
||||||
# Needed for at least Wrap.
|
# Needed for at least Wrap.
|
||||||
for d in descriptors:
|
for d in descriptors:
|
||||||
|
# If this is a generated iterator interface, we only create these
|
||||||
|
# in the generated bindings, and don't need to forward declare.
|
||||||
|
if d.interface.isIteratorInterface():
|
||||||
|
continue
|
||||||
builder.add(d.nativeType)
|
builder.add(d.nativeType)
|
||||||
# If we're an interface and we have a maplike/setlike declaration,
|
# If we're an interface and we have a maplike/setlike declaration,
|
||||||
# we'll have helper functions exposed to the native side of our
|
# we'll have helper functions exposed to the native side of our
|
||||||
@ -12786,17 +12809,23 @@ class CGForwardDeclarations(CGWrapper):
|
|||||||
# our key/value types are interfaces, they'll be passed as
|
# our key/value types are interfaces, they'll be passed as
|
||||||
# arguments to helper functions, and they'll need to be forward
|
# arguments to helper functions, and they'll need to be forward
|
||||||
# declared in the header.
|
# declared in the header.
|
||||||
if d.interface.maplikeOrSetlike:
|
if d.interface.maplikeOrSetlikeOrIterable:
|
||||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlike.keyType,
|
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.keyType,
|
||||||
config)
|
|
||||||
builder.forwardDeclareForType(d.interface.maplikeOrSetlike.valueType,
|
|
||||||
config)
|
config)
|
||||||
|
if d.interface.maplikeOrSetlikeOrIterable.hasValueType():
|
||||||
|
builder.forwardDeclareForType(d.interface.maplikeOrSetlikeOrIterable.valueType,
|
||||||
|
config)
|
||||||
|
|
||||||
# We just about always need NativePropertyHooks
|
# We just about always need NativePropertyHooks
|
||||||
builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
|
builder.addInMozillaDom("NativePropertyHooks", isStruct=True)
|
||||||
builder.addInMozillaDom("ProtoAndIfaceCache")
|
builder.addInMozillaDom("ProtoAndIfaceCache")
|
||||||
# Add the atoms cache type, even if we don't need it.
|
# Add the atoms cache type, even if we don't need it.
|
||||||
for d in descriptors:
|
for d in descriptors:
|
||||||
|
# Iterators have native types that are template classes, so
|
||||||
|
# creating an 'Atoms' cache type doesn't work for them, and is one
|
||||||
|
# of the cases where we don't need it anyways.
|
||||||
|
if d.interface.isIteratorInterface():
|
||||||
|
continue
|
||||||
builder.add(d.nativeType + "Atoms", isStruct=True)
|
builder.add(d.nativeType + "Atoms", isStruct=True)
|
||||||
|
|
||||||
for callback in mainCallbacks:
|
for callback in mainCallbacks:
|
||||||
@ -12859,6 +12888,8 @@ class CGBindingRoot(CGThing):
|
|||||||
|
|
||||||
bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
|
bindingDeclareHeaders["mozilla/dom/UnionMember.h"] = len(unionStructs) > 0
|
||||||
bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = len(unionStructs) > 0
|
bindingDeclareHeaders["mozilla/dom/BindingUtils.h"] = len(unionStructs) > 0
|
||||||
|
bindingDeclareHeaders["mozilla/dom/IterableIterator.h"] = any(d.interface.isIteratorInterface() or
|
||||||
|
d.interface.isIterable() for d in descriptors)
|
||||||
|
|
||||||
def descriptorHasCrossOriginProperties(desc):
|
def descriptorHasCrossOriginProperties(desc):
|
||||||
def hasCrossOriginProperty(m):
|
def hasCrossOriginProperty(m):
|
||||||
@ -12922,14 +12953,6 @@ class CGBindingRoot(CGThing):
|
|||||||
bindingHeaders["nsIGlobalObject.h"] = jsImplemented
|
bindingHeaders["nsIGlobalObject.h"] = jsImplemented
|
||||||
bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
|
bindingHeaders["AtomList.h"] = hasNonEmptyDictionaries or jsImplemented or callbackDescriptors
|
||||||
|
|
||||||
def addHeaderBasedOnTypes(header, typeChecker):
|
|
||||||
bindingHeaders[header] = (
|
|
||||||
bindingHeaders.get(header, False) or
|
|
||||||
any(map(typeChecker,
|
|
||||||
getAllTypes(descriptors + callbackDescriptors,
|
|
||||||
dictionaries,
|
|
||||||
mainCallbacks + workerCallbacks))))
|
|
||||||
|
|
||||||
# Only mainthread things can have hasXPConnectImpls
|
# Only mainthread things can have hasXPConnectImpls
|
||||||
provider = config.getDescriptorProvider(False)
|
provider = config.getDescriptorProvider(False)
|
||||||
|
|
||||||
@ -14966,7 +14989,7 @@ def getMaplikeOrSetlikeBackingObject(descriptor, maplikeOrSetlike, helperImpl=No
|
|||||||
Generate code to get/create a JS backing object for a maplike/setlike
|
Generate code to get/create a JS backing object for a maplike/setlike
|
||||||
declaration from the declaration slot.
|
declaration from the declaration slot.
|
||||||
"""
|
"""
|
||||||
func_prefix = maplikeOrSetlike.maplikeOrSetlikeType.title()
|
func_prefix = maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title()
|
||||||
ret = fill(
|
ret = fill(
|
||||||
"""
|
"""
|
||||||
JS::Rooted<JSObject*> backingObj(cx);
|
JS::Rooted<JSObject*> backingObj(cx);
|
||||||
@ -15403,8 +15426,11 @@ class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, descriptor, maplikeOrSetlike):
|
def __init__(self, descriptor, maplikeOrSetlike):
|
||||||
self.descriptor = descriptor
|
self.descriptor = descriptor
|
||||||
|
# Since iterables are folded in with maplike/setlike, make sure we've
|
||||||
|
# got the right type here.
|
||||||
|
assert maplikeOrSetlike.isMaplike() or maplikeOrSetlike.isSetlike()
|
||||||
self.maplikeOrSetlike = maplikeOrSetlike
|
self.maplikeOrSetlike = maplikeOrSetlike
|
||||||
self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeType.title())
|
self.namespace = "%sHelpers" % (self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType.title())
|
||||||
self.helpers = [
|
self.helpers = [
|
||||||
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
CGMaplikeOrSetlikeHelperFunctionGenerator(descriptor,
|
||||||
maplikeOrSetlike,
|
maplikeOrSetlike,
|
||||||
@ -15436,6 +15462,26 @@ class CGMaplikeOrSetlikeHelperGenerator(CGNamespace):
|
|||||||
CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
|
CGNamespace.__init__(self, self.namespace, CGList(self.helpers))
|
||||||
|
|
||||||
|
|
||||||
|
class CGIterableMethodGenerator(CGGeneric):
|
||||||
|
"""
|
||||||
|
Creates methods for iterable interfaces. Unwrapping/wrapping
|
||||||
|
will be taken care of by the usual method generation machinery in
|
||||||
|
CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
|
||||||
|
using CGCallGenerator.
|
||||||
|
"""
|
||||||
|
def __init__(self, descriptor, iterable, methodName):
|
||||||
|
CGGeneric.__init__(self, fill(
|
||||||
|
"""
|
||||||
|
typedef IterableIterator<${nativeType}> itrType;
|
||||||
|
nsRefPtr<itrType> result(new itrType(self,
|
||||||
|
itrType::IterableIteratorType::${itrMethod},
|
||||||
|
&${ifaceName}IteratorBinding::Wrap));
|
||||||
|
""",
|
||||||
|
nativeType=descriptor.nativeType,
|
||||||
|
ifaceName=descriptor.interface.identifier.name,
|
||||||
|
itrMethod=methodName.title()))
|
||||||
|
|
||||||
|
|
||||||
class GlobalGenRoots():
|
class GlobalGenRoots():
|
||||||
"""
|
"""
|
||||||
Roots for global codegen.
|
Roots for global codegen.
|
||||||
|
@ -26,6 +26,7 @@ class Configuration:
|
|||||||
# |parseData|.
|
# |parseData|.
|
||||||
self.descriptors = []
|
self.descriptors = []
|
||||||
self.interfaces = {}
|
self.interfaces = {}
|
||||||
|
self.descriptorsByName = {}
|
||||||
self.optimizedOutDescriptorNames = set()
|
self.optimizedOutDescriptorNames = set()
|
||||||
self.generatedEvents = generatedEvents
|
self.generatedEvents = generatedEvents
|
||||||
self.maxProtoChainLength = 0
|
self.maxProtoChainLength = 0
|
||||||
@ -86,15 +87,18 @@ class Configuration:
|
|||||||
else:
|
else:
|
||||||
raise TypeError("Interface " + iface.identifier.name +
|
raise TypeError("Interface " + iface.identifier.name +
|
||||||
" should have no more than two entries in Bindings.conf")
|
" should have no more than two entries in Bindings.conf")
|
||||||
self.descriptors.extend([Descriptor(self, iface, x) for x in entry])
|
descs = [Descriptor(self, iface, x) for x in entry]
|
||||||
|
self.descriptors.extend(descs)
|
||||||
|
# Setting up descriptorsByName while iterating through interfaces
|
||||||
|
# means we can get the nativeType of iterable interfaces without
|
||||||
|
# having to do multiple loops.
|
||||||
|
for d in descs:
|
||||||
|
self.descriptorsByName.setdefault(d.interface.identifier.name,
|
||||||
|
[]).append(d)
|
||||||
|
|
||||||
# Keep the descriptor list sorted for determinism.
|
# Keep the descriptor list sorted for determinism.
|
||||||
self.descriptors.sort(lambda x, y: cmp(x.name, y.name))
|
self.descriptors.sort(lambda x, y: cmp(x.name, y.name))
|
||||||
|
|
||||||
self.descriptorsByName = {}
|
|
||||||
for d in self.descriptors:
|
|
||||||
self.descriptorsByName.setdefault(d.interface.identifier.name,
|
|
||||||
[]).append(d)
|
|
||||||
|
|
||||||
self.descriptorsByFile = {}
|
self.descriptorsByFile = {}
|
||||||
for d in self.descriptors:
|
for d in self.descriptors:
|
||||||
@ -348,7 +352,18 @@ class Descriptor(DescriptorProvider):
|
|||||||
|
|
||||||
# Read the desc, and fill in the relevant defaults.
|
# Read the desc, and fill in the relevant defaults.
|
||||||
ifaceName = self.interface.identifier.name
|
ifaceName = self.interface.identifier.name
|
||||||
if self.interface.isExternal():
|
# For generated iterator interfaces for other iterable interfaces, we
|
||||||
|
# just use IterableIterator as the native type, templated on the
|
||||||
|
# nativeType of the iterable interface. That way we can have a
|
||||||
|
# templated implementation for all the duplicated iterator
|
||||||
|
# functionality.
|
||||||
|
if self.interface.isIteratorInterface():
|
||||||
|
itrName = self.interface.iterableInterface.identifier.name
|
||||||
|
itrDesc = self.getDescriptor(itrName)
|
||||||
|
nativeTypeDefault = ("mozilla::dom::IterableIterator<%s>"
|
||||||
|
% itrDesc.nativeType)
|
||||||
|
|
||||||
|
elif self.interface.isExternal():
|
||||||
assert not self.workers
|
assert not self.workers
|
||||||
nativeTypeDefault = "nsIDOM" + ifaceName
|
nativeTypeDefault = "nsIDOM" + ifaceName
|
||||||
elif self.interface.isCallback():
|
elif self.interface.isCallback():
|
||||||
@ -386,6 +401,8 @@ class Descriptor(DescriptorProvider):
|
|||||||
headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
|
headerDefault = "mozilla/dom/workers/bindings/%s.h" % ifaceName
|
||||||
elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
|
elif not self.interface.isExternal() and self.interface.getExtendedAttribute("HeaderFile"):
|
||||||
headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
|
headerDefault = self.interface.getExtendedAttribute("HeaderFile")[0]
|
||||||
|
elif self.interface.isIteratorInterface():
|
||||||
|
headerDefault = "mozilla/dom/IterableIterator.h"
|
||||||
else:
|
else:
|
||||||
headerDefault = self.nativeType
|
headerDefault = self.nativeType
|
||||||
headerDefault = headerDefault.replace("::", "/") + ".h"
|
headerDefault = headerDefault.replace("::", "/") + ".h"
|
||||||
@ -514,6 +531,7 @@ class Descriptor(DescriptorProvider):
|
|||||||
if desc.get('wantsQI', None) is not None:
|
if desc.get('wantsQI', None) is not None:
|
||||||
self._wantsQI = desc.get('wantsQI', None)
|
self._wantsQI = desc.get('wantsQI', None)
|
||||||
self.wrapperCache = (not self.interface.isCallback() and
|
self.wrapperCache = (not self.interface.isCallback() and
|
||||||
|
not self.interface.isIteratorInterface() and
|
||||||
desc.get('wrapperCache', True))
|
desc.get('wrapperCache', True))
|
||||||
|
|
||||||
def make_name(name):
|
def make_name(name):
|
||||||
|
35
dom/bindings/IterableIterator.cpp
Normal file
35
dom/bindings/IterableIterator.cpp
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* -*- 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 "mozilla/dom/IterableIterator.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
// Due to IterableIterator being a templated class, we implement the necessary
|
||||||
|
// CC bits in a superclass that IterableIterator then inherits from. This allows
|
||||||
|
// us to put the macros outside of the header. The base class has pure virtual
|
||||||
|
// functions for Traverse/Unlink that the templated subclasses will override.
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(IterableIteratorBase)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(IterableIteratorBase)
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IterableIteratorBase)
|
||||||
|
tmp->TraverseHelper(cb);
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IterableIteratorBase)
|
||||||
|
tmp->UnlinkHelper();
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IterableIteratorBase)
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
192
dom/bindings/IterableIterator.h
Normal file
192
dom/bindings/IterableIterator.h
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
/* -*- 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/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The IterableIterator class is used for WebIDL interfaces that have a
|
||||||
|
* iterable<> member defined. It handles the ES6 Iterator-like functions that
|
||||||
|
* are generated for the iterable interface.
|
||||||
|
*
|
||||||
|
* For iterable interfaces, the implementation class will need to contain three
|
||||||
|
* functions:
|
||||||
|
*
|
||||||
|
* - size_t GetIterableLength()
|
||||||
|
* - Returns the number of elements available to iterate over
|
||||||
|
* - [type] GetKeyAtIndex(size_t index)
|
||||||
|
* - Returns the key at the requested index
|
||||||
|
* - [type] GetValueAtIndex(size_t index)
|
||||||
|
* - Returns the value at the requested index, or the key again if this is
|
||||||
|
* a single type iterator.
|
||||||
|
*
|
||||||
|
* Examples of iterable interface implementations can be found in the bindings
|
||||||
|
* test directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_IterableIterator_h
|
||||||
|
#define mozilla_dom_IterableIterator_h
|
||||||
|
|
||||||
|
#include "nsISupports.h"
|
||||||
|
#include "nsWrapperCache.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "mozilla/dom/ToJSValue.h"
|
||||||
|
#include "jswrapper.h"
|
||||||
|
#include "mozilla/dom/IterableIteratorBinding.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class IterableIteratorBase : public nsISupports
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS(IterableIteratorBase)
|
||||||
|
typedef enum {
|
||||||
|
Keys = 0,
|
||||||
|
Values,
|
||||||
|
Entries
|
||||||
|
} IterableIteratorType;
|
||||||
|
|
||||||
|
IterableIteratorBase() {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~IterableIteratorBase() {}
|
||||||
|
virtual void UnlinkHelper() = 0;
|
||||||
|
virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class IterableIterator final : public IterableIteratorBase
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
typedef bool (*WrapFunc)(JSContext* aCx,
|
||||||
|
mozilla::dom::IterableIterator<T>* aObject,
|
||||||
|
JS::Handle<JSObject*> aGivenProto,
|
||||||
|
JS::MutableHandle<JSObject*> aReflector);
|
||||||
|
IterableIterator(T* aIterableObj, IterableIteratorType aIteratorType, WrapFunc aWrapFunc)
|
||||||
|
: mIteratorType(aIteratorType)
|
||||||
|
, mIterableObj(aIterableObj)
|
||||||
|
, mIndex(0)
|
||||||
|
, mWrapFunc(aWrapFunc)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(mIterableObj);
|
||||||
|
MOZ_ASSERT(mWrapFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
DictReturn(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
|
||||||
|
bool aDone, JS::Handle<JS::Value> aValue, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
RootedDictionary<IterableKeyOrValueResult> dict(aCx);
|
||||||
|
dict.mDone = aDone;
|
||||||
|
dict.mValue = aValue;
|
||||||
|
JS::Rooted<JS::Value> dictValue(aCx);
|
||||||
|
if (!ToJSValue(aCx, dict, &dictValue)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aResult.set(&dictValue.toObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Next(JSContext* aCx, JS::MutableHandle<JSObject*> aResult, ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
JS::Rooted<JS::Value> value(aCx, JS::UndefinedValue());
|
||||||
|
if (mIndex >= mIterableObj->GetIterableLength()) {
|
||||||
|
DictReturn(aCx, aResult, true, value, aRv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (mIteratorType) {
|
||||||
|
case IterableIteratorType::Keys:
|
||||||
|
{
|
||||||
|
if (!ToJSValue(aCx, mIterableObj->GetKeyAtIndex(mIndex), &value)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DictReturn(aCx, aResult, false, value, aRv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IterableIteratorType::Values:
|
||||||
|
{
|
||||||
|
if (!ToJSValue(aCx, mIterableObj->GetValueAtIndex(mIndex), &value)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DictReturn(aCx, aResult, false, value, aRv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IterableIteratorType::Entries:
|
||||||
|
{
|
||||||
|
JS::Rooted<JS::Value> key(aCx);
|
||||||
|
if (!ToJSValue(aCx, mIterableObj->GetKeyAtIndex(mIndex), &key)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ToJSValue(aCx, mIterableObj->GetValueAtIndex(mIndex), &value)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RootedDictionary<IterableKeyAndValueResult> dict(aCx);
|
||||||
|
dict.mDone = false;
|
||||||
|
// Dictionary values are a Sequence, which is a FallibleTArray, so we need
|
||||||
|
// to check returns when appending.
|
||||||
|
if (!dict.mValue.AppendElement(key, mozilla::fallible)) {
|
||||||
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!dict.mValue.AppendElement(value, mozilla::fallible)) {
|
||||||
|
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JS::Rooted<JS::Value> dictValue(aCx);
|
||||||
|
if (!ToJSValue(aCx, dict, &dictValue)) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aResult.set(&dictValue.toObject());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Invalid iterator type!");
|
||||||
|
}
|
||||||
|
++mIndex;
|
||||||
|
}
|
||||||
|
virtual ~IterableIterator() {}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aObj)
|
||||||
|
{
|
||||||
|
return (*mWrapFunc)(aCx, this, aGivenProto, aObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Tells whether this is a key, value, or entries iterator.
|
||||||
|
IterableIteratorType mIteratorType;
|
||||||
|
// Binding Implementation Object that we're iterating over.
|
||||||
|
nsRefPtr<T> mIterableObj;
|
||||||
|
// Current index of iteration.
|
||||||
|
uint32_t mIndex;
|
||||||
|
// Function pointer to binding-type-specific Wrap() call for this iterator.
|
||||||
|
WrapFunc mWrapFunc;
|
||||||
|
|
||||||
|
// Since we're templated on a binding, we need to possibly CC it, but can't do
|
||||||
|
// that through macros. So it happens here.
|
||||||
|
virtual void UnlinkHelper() final
|
||||||
|
{
|
||||||
|
mIterableObj = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void TraverseHelper(nsCycleCollectionTraversalCallback& cb) override
|
||||||
|
{
|
||||||
|
IterableIterator<T>* tmp = this;
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIterableObj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_IterableIterator_h
|
@ -27,6 +27,7 @@ EXPORTS.mozilla.dom += [
|
|||||||
'DOMString.h',
|
'DOMString.h',
|
||||||
'Errors.msg',
|
'Errors.msg',
|
||||||
'Exceptions.h',
|
'Exceptions.h',
|
||||||
|
'IterableIterator.h',
|
||||||
'JSSlots.h',
|
'JSSlots.h',
|
||||||
'MozMap.h',
|
'MozMap.h',
|
||||||
'NonRefcountedDOMObject.h',
|
'NonRefcountedDOMObject.h',
|
||||||
@ -74,6 +75,7 @@ UNIFIED_SOURCES += [
|
|||||||
'Date.cpp',
|
'Date.cpp',
|
||||||
'DOMJSProxyHandler.cpp',
|
'DOMJSProxyHandler.cpp',
|
||||||
'Exceptions.cpp',
|
'Exceptions.cpp',
|
||||||
|
'IterableIterator.cpp',
|
||||||
'ToJSValue.cpp',
|
'ToJSValue.cpp',
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -88,12 +90,16 @@ SOURCES += [
|
|||||||
# them are only run in debug mode.
|
# them are only run in debug mode.
|
||||||
if CONFIG['MOZ_DEBUG']:
|
if CONFIG['MOZ_DEBUG']:
|
||||||
EXPORTS.mozilla.dom += [
|
EXPORTS.mozilla.dom += [
|
||||||
|
"test/TestInterfaceIterableDouble.h",
|
||||||
|
"test/TestInterfaceIterableSingle.h",
|
||||||
"test/TestInterfaceMaplike.h",
|
"test/TestInterfaceMaplike.h",
|
||||||
"test/TestInterfaceMaplikeObject.h",
|
"test/TestInterfaceMaplikeObject.h",
|
||||||
"test/TestInterfaceSetlike.h",
|
"test/TestInterfaceSetlike.h",
|
||||||
"test/TestInterfaceSetlikeNode.h"
|
"test/TestInterfaceSetlikeNode.h"
|
||||||
]
|
]
|
||||||
UNIFIED_SOURCES += [
|
UNIFIED_SOURCES += [
|
||||||
|
"test/TestInterfaceIterableDouble.cpp",
|
||||||
|
"test/TestInterfaceIterableSingle.cpp",
|
||||||
"test/TestInterfaceMaplike.cpp",
|
"test/TestInterfaceMaplike.cpp",
|
||||||
"test/TestInterfaceMaplikeObject.cpp",
|
"test/TestInterfaceMaplikeObject.cpp",
|
||||||
"test/TestInterfaceSetlike.cpp",
|
"test/TestInterfaceSetlike.cpp",
|
||||||
|
@ -540,6 +540,9 @@ class IDLExternalInterface(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def isIteratorInterface(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def isExternal(self):
|
def isExternal(self):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -640,7 +643,7 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
self._callback = False
|
self._callback = False
|
||||||
self._finished = False
|
self._finished = False
|
||||||
self.members = []
|
self.members = []
|
||||||
self.maplikeOrSetlike = None
|
self.maplikeOrSetlikeOrIterable = None
|
||||||
self._partialInterfaces = []
|
self._partialInterfaces = []
|
||||||
self._extendedAttrDict = {}
|
self._extendedAttrDict = {}
|
||||||
# namedConstructors needs deterministic ordering because bindings code
|
# namedConstructors needs deterministic ordering because bindings code
|
||||||
@ -664,6 +667,9 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
self.totalMembersInSlots = 0
|
self.totalMembersInSlots = 0
|
||||||
# Tracking of the number of own own members we have in slots
|
# Tracking of the number of own own members we have in slots
|
||||||
self._ownMembersInSlots = 0
|
self._ownMembersInSlots = 0
|
||||||
|
# If this is an iterator interface, we need to know what iterable
|
||||||
|
# interface we're iterating for in order to get its nativeType.
|
||||||
|
self.iterableInterface = None
|
||||||
|
|
||||||
IDLObjectWithScope.__init__(self, location, parentScope, name)
|
IDLObjectWithScope.__init__(self, location, parentScope, name)
|
||||||
IDLExposureMixins.__init__(self, location)
|
IDLExposureMixins.__init__(self, location)
|
||||||
@ -682,6 +688,13 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def isIterable(self):
|
||||||
|
return (self.maplikeOrSetlikeOrIterable and
|
||||||
|
self.maplikeOrSetlikeOrIterable.isIterable())
|
||||||
|
|
||||||
|
def isIteratorInterface(self):
|
||||||
|
return self.iterableInterface is not None
|
||||||
|
|
||||||
def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
|
def resolveIdentifierConflict(self, scope, identifier, originalObject, newObject):
|
||||||
assert isinstance(scope, IDLScope)
|
assert isinstance(scope, IDLScope)
|
||||||
assert isinstance(originalObject, IDLInterfaceMember)
|
assert isinstance(originalObject, IDLInterfaceMember)
|
||||||
@ -718,22 +731,22 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
# need to be treated like regular interface members, do this before
|
# need to be treated like regular interface members, do this before
|
||||||
# things like exposure setting.
|
# things like exposure setting.
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
if member.isMaplikeOrSetlike():
|
if member.isMaplikeOrSetlikeOrIterable():
|
||||||
# Check that we only have one interface declaration (currently
|
# Check that we only have one interface declaration (currently
|
||||||
# there can only be one maplike/setlike declaration per
|
# there can only be one maplike/setlike declaration per
|
||||||
# interface)
|
# interface)
|
||||||
if self.maplikeOrSetlike:
|
if self.maplikeOrSetlikeOrIterable:
|
||||||
raise WebIDLError("%s declaration used on "
|
raise WebIDLError("%s declaration used on "
|
||||||
"interface that already has %s "
|
"interface that already has %s "
|
||||||
"declaration" %
|
"declaration" %
|
||||||
(member.maplikeOrSetlikeType,
|
(member.maplikeOrSetlikeOrIterableType,
|
||||||
self.maplikeOrSetlike.maplikeOrSetlikeType),
|
self.maplikeOrSetlike.maplikeOrSetlikeOrIterableType),
|
||||||
[self.maplikeOrSetlike.location,
|
[self.maplikeOrSetlikeOrIterable.location,
|
||||||
member.location])
|
member.location])
|
||||||
self.maplikeOrSetlike = member
|
self.maplikeOrSetlikeOrIterable = member
|
||||||
# If we've got a maplike or setlike declaration, we'll be building all of
|
# If we've got a maplike or setlike declaration, we'll be building all of
|
||||||
# our required methods in Codegen. Generate members now.
|
# our required methods in Codegen. Generate members now.
|
||||||
self.maplikeOrSetlike.expand(self.members, self.isJSImplemented())
|
self.maplikeOrSetlikeOrIterable.expand(self.members, self.isJSImplemented())
|
||||||
|
|
||||||
# Now that we've merged in our partial interfaces, set the
|
# Now that we've merged in our partial interfaces, set the
|
||||||
# _exposureGlobalNames on any members that don't have it set yet. Note
|
# _exposureGlobalNames on any members that don't have it set yet. Note
|
||||||
@ -884,14 +897,14 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
|
|
||||||
# If we have a maplike or setlike, and the consequential interface
|
# If we have a maplike or setlike, and the consequential interface
|
||||||
# also does, throw an error.
|
# also does, throw an error.
|
||||||
if iface.maplikeOrSetlike and self.maplikeOrSetlike:
|
if iface.maplikeOrSetlikeOrIterable and self.maplikeOrSetlikeOrIterable:
|
||||||
raise WebIDLError("Maplike/setlike interface %s cannot have "
|
raise WebIDLError("Maplike/setlike/iterable interface %s cannot have "
|
||||||
"maplike/setlike interface %s as a "
|
"maplike/setlike/iterable interface %s as a "
|
||||||
"consequential interface" %
|
"consequential interface" %
|
||||||
(self.identifier.name,
|
(self.identifier.name,
|
||||||
iface.identifier.name),
|
iface.identifier.name),
|
||||||
[self.maplikeOrSetlike.location,
|
[self.maplikeOrSetlikeOrIterable.location,
|
||||||
iface.maplikeOrSetlike.location])
|
iface.maplikeOrSetlikeOrIterable.location])
|
||||||
additionalMembers = iface.originalMembers
|
additionalMembers = iface.originalMembers
|
||||||
for additionalMember in additionalMembers:
|
for additionalMember in additionalMembers:
|
||||||
for member in self.members:
|
for member in self.members:
|
||||||
@ -905,15 +918,15 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
|
|
||||||
for ancestor in self.getInheritedInterfaces():
|
for ancestor in self.getInheritedInterfaces():
|
||||||
ancestor.interfacesBasedOnSelf.add(self)
|
ancestor.interfacesBasedOnSelf.add(self)
|
||||||
if (ancestor.maplikeOrSetlike is not None and
|
if (ancestor.maplikeOrSetlikeOrIterable is not None and
|
||||||
self.maplikeOrSetlike is not None):
|
self.maplikeOrSetlikeOrIterable is not None):
|
||||||
raise WebIDLError("Cannot have maplike/setlike on %s that "
|
raise WebIDLError("Cannot have maplike/setlike on %s that "
|
||||||
"inherits %s, which is already "
|
"inherits %s, which is already "
|
||||||
"maplike/setlike" %
|
"maplike/setlike" %
|
||||||
(self.identifier.name,
|
(self.identifier.name,
|
||||||
ancestor.identifier.name),
|
ancestor.identifier.name),
|
||||||
[self.maplikeOrSetlike.location,
|
[self.maplikeOrSetlikeOrIterable.location,
|
||||||
ancestor.maplikeOrSetlike.location])
|
ancestor.maplikeOrSetlikeOrIterable.location])
|
||||||
for ancestorConsequential in ancestor.getConsequentialInterfaces():
|
for ancestorConsequential in ancestor.getConsequentialInterfaces():
|
||||||
ancestorConsequential.interfacesBasedOnSelf.add(self)
|
ancestorConsequential.interfacesBasedOnSelf.add(self)
|
||||||
|
|
||||||
@ -997,12 +1010,12 @@ class IDLInterface(IDLObjectWithScope, IDLExposureMixins):
|
|||||||
# At this point, we have all of our members. If the current interface
|
# At this point, we have all of our members. If the current interface
|
||||||
# uses maplike/setlike, check for collisions anywhere in the current
|
# uses maplike/setlike, check for collisions anywhere in the current
|
||||||
# interface or higher in the inheritance chain.
|
# interface or higher in the inheritance chain.
|
||||||
if self.maplikeOrSetlike:
|
if self.maplikeOrSetlikeOrIterable:
|
||||||
testInterface = self
|
testInterface = self
|
||||||
isAncestor = False
|
isAncestor = False
|
||||||
while testInterface:
|
while testInterface:
|
||||||
self.maplikeOrSetlike.checkCollisions(testInterface.members,
|
self.maplikeOrSetlikeOrIterable.checkCollisions(testInterface.members,
|
||||||
isAncestor)
|
isAncestor)
|
||||||
isAncestor = True
|
isAncestor = True
|
||||||
testInterface = testInterface.parent
|
testInterface = testInterface.parent
|
||||||
|
|
||||||
@ -3367,7 +3380,8 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||||||
'Const',
|
'Const',
|
||||||
'Attr',
|
'Attr',
|
||||||
'Method',
|
'Method',
|
||||||
'MaplikeOrSetlike'
|
'MaplikeOrSetlike',
|
||||||
|
'Iterable'
|
||||||
)
|
)
|
||||||
|
|
||||||
Special = enum(
|
Special = enum(
|
||||||
@ -3393,6 +3407,10 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||||||
def isConst(self):
|
def isConst(self):
|
||||||
return self.tag == IDLInterfaceMember.Tags.Const
|
return self.tag == IDLInterfaceMember.Tags.Const
|
||||||
|
|
||||||
|
def isMaplikeOrSetlikeOrIterable(self):
|
||||||
|
return (self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike or
|
||||||
|
self.tag == IDLInterfaceMember.Tags.Iterable)
|
||||||
|
|
||||||
def isMaplikeOrSetlike(self):
|
def isMaplikeOrSetlike(self):
|
||||||
return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
|
return self.tag == IDLInterfaceMember.Tags.MaplikeOrSetlike
|
||||||
|
|
||||||
@ -3470,58 +3488,42 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
|
|||||||
self.aliases.append(alias)
|
self.aliases.append(alias)
|
||||||
|
|
||||||
|
|
||||||
# MaplikeOrSetlike adds a trait to an interface, like map or iteration
|
class IDLMaplikeOrSetlikeOrIterableBase(IDLInterfaceMember):
|
||||||
# functions. To handle them while still getting all of the generated binding
|
|
||||||
# code taken care of, we treat them as macros that are expanded into members
|
|
||||||
# based on parsed values.
|
|
||||||
class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
|
||||||
|
|
||||||
MaplikeOrSetlikeTypes = enum(
|
|
||||||
'maplike',
|
|
||||||
'setlike'
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(self, location, identifier, maplikeOrSetlikeType,
|
|
||||||
readonly, keyType, valueType):
|
|
||||||
IDLInterfaceMember.__init__(self, location, identifier,
|
|
||||||
IDLInterfaceMember.Tags.MaplikeOrSetlike)
|
|
||||||
|
|
||||||
|
def __init__(self, location, identifier, ifaceType, keyType, valueType, ifaceKind):
|
||||||
|
IDLInterfaceMember.__init__(self, location, identifier, ifaceKind)
|
||||||
assert isinstance(keyType, IDLType)
|
assert isinstance(keyType, IDLType)
|
||||||
assert isinstance(valueType, IDLType)
|
assert ifaceType in ['maplike', 'setlike', 'iterable']
|
||||||
self.maplikeOrSetlikeType = maplikeOrSetlikeType
|
if valueType is not None:
|
||||||
self.readonly = readonly
|
assert isinstance(valueType, IDLType)
|
||||||
self.keyType = keyType
|
self.keyType = keyType
|
||||||
self.valueType = valueType
|
self.valueType = valueType
|
||||||
self.slotIndex = None
|
self.maplikeOrSetlikeOrIterableType = ifaceType
|
||||||
self.disallowedMemberNames = []
|
self.disallowedMemberNames = []
|
||||||
self.disallowedNonMethodNames = []
|
self.disallowedNonMethodNames = []
|
||||||
|
|
||||||
# When generating JSAPI access code, we need to know the backing object
|
|
||||||
# type prefix to create the correct function. Generate here for reuse.
|
|
||||||
if self.isMaplike():
|
|
||||||
self.prefix = 'Map'
|
|
||||||
elif self.isSetlike():
|
|
||||||
self.prefix = 'Set'
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeType, self.keyType)
|
|
||||||
|
|
||||||
def isMaplike(self):
|
def isMaplike(self):
|
||||||
return self.maplikeOrSetlikeType == "maplike"
|
return self.maplikeOrSetlikeOrIterableType == "maplike"
|
||||||
|
|
||||||
def isSetlike(self):
|
def isSetlike(self):
|
||||||
return self.maplikeOrSetlikeType == "setlike"
|
return self.maplikeOrSetlikeOrIterableType == "setlike"
|
||||||
|
|
||||||
|
def isIterable(self):
|
||||||
|
return self.maplikeOrSetlikeOrIterableType == "iterable"
|
||||||
|
|
||||||
|
def hasValueType(self):
|
||||||
|
return self.valueType is not None
|
||||||
|
|
||||||
def checkCollisions(self, members, isAncestor):
|
def checkCollisions(self, members, isAncestor):
|
||||||
for member in members:
|
for member in members:
|
||||||
# Check that there are no disallowed members
|
# Check that there are no disallowed members
|
||||||
if (member.identifier.name in self.disallowedMemberNames and
|
if (member.identifier.name in self.disallowedMemberNames and
|
||||||
not ((member.isMethod() and member.isMaplikeOrSetlikeMethod()) or
|
not ((member.isMethod() and member.isMaplikeOrSetlikeOrIterableMethod()) or
|
||||||
(member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
|
(member.isAttr() and member.isMaplikeOrSetlikeAttr()))):
|
||||||
raise WebIDLError("Member '%s' conflicts "
|
raise WebIDLError("Member '%s' conflicts "
|
||||||
"with reserved %s name." %
|
"with reserved %s name." %
|
||||||
(member.identifier.name,
|
(member.identifier.name,
|
||||||
self.maplikeOrSetlikeType),
|
self.maplikeOrSetlikeOrIterableType),
|
||||||
[self.location, member.location])
|
[self.location, member.location])
|
||||||
# Check that there are no disallowed non-method members
|
# Check that there are no disallowed non-method members
|
||||||
if (isAncestor or (member.isAttr() or member.isConst()) and
|
if (isAncestor or (member.isAttr() or member.isConst()) and
|
||||||
@ -3529,168 +3531,76 @@ class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
|||||||
raise WebIDLError("Member '%s' conflicts "
|
raise WebIDLError("Member '%s' conflicts "
|
||||||
"with reserved %s method." %
|
"with reserved %s method." %
|
||||||
(member.identifier.name,
|
(member.identifier.name,
|
||||||
self.maplikeOrSetlikeType),
|
self.maplikeOrSetlikeOrIterableType),
|
||||||
[self.location, member.location])
|
[self.location, member.location])
|
||||||
|
|
||||||
def expand(self, members, isJSImplemented):
|
def addMethod(self, name, members, allowExistingOperations, returnType, args=[],
|
||||||
|
chromeOnly=False, isPure=False, affectsNothing=False, newObject=False):
|
||||||
"""
|
"""
|
||||||
In order to take advantage of all of the method machinery in Codegen,
|
Create an IDLMethod based on the parameters passed in.
|
||||||
we generate our functions as if they were part of the interface
|
|
||||||
specification during parsing.
|
- members is the member list to add this function to, since this is
|
||||||
|
called during the member expansion portion of interface object
|
||||||
|
building.
|
||||||
|
|
||||||
|
- chromeOnly is only True for read-only js implemented classes, to
|
||||||
|
implement underscore prefixed convenience functions which would
|
||||||
|
otherwise not be available, unlike the case of C++ bindings.
|
||||||
|
|
||||||
|
- isPure is only True for idempotent functions, so it is not valid for
|
||||||
|
things like keys, values, etc. that return a new object every time.
|
||||||
|
|
||||||
|
- affectsNothing means that nothing changes due to this method, which
|
||||||
|
affects JIT optimization behavior
|
||||||
|
|
||||||
|
- newObject means the method creates and returns a new object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def addMethod(name, allowExistingOperations, returnType, args=[],
|
# Only add name to lists for collision checks if it's not chrome
|
||||||
chromeOnly=False, isPure=False, affectsNothing=False):
|
# only.
|
||||||
"""
|
if chromeOnly:
|
||||||
Create an IDLMethod based on the parameters passed in. chromeOnly is only
|
name = "__" + name
|
||||||
True for read-only js implemented classes, to implement underscore
|
else:
|
||||||
prefixed convenience functions would otherwise not be available,
|
if not allowExistingOperations:
|
||||||
unlike the case of C++ bindings. isPure is only True for
|
self.disallowedMemberNames.append(name)
|
||||||
idempotent functions, so it is not valid for things like keys,
|
|
||||||
values, etc. that return a new object every time.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Only add name to lists for collision checks if it's not chrome
|
|
||||||
# only.
|
|
||||||
if chromeOnly:
|
|
||||||
name = "__" + name
|
|
||||||
else:
|
else:
|
||||||
if not allowExistingOperations:
|
self.disallowedNonMethodNames.append(name)
|
||||||
self.disallowedMemberNames.append(name)
|
# If allowExistingOperations is True, and another operation exists
|
||||||
else:
|
# with the same name as the one we're trying to add, don't add the
|
||||||
self.disallowedNonMethodNames.append(name)
|
# maplike/setlike operation. However, if the operation is static,
|
||||||
|
# then fail by way of creating the function, which will cause a
|
||||||
# If allowExistingOperations is True, and another operation exists
|
# naming conflict, per the spec.
|
||||||
# with the same name as the one we're trying to add, don't add the
|
if allowExistingOperations:
|
||||||
# maplike/setlike operation. However, if the operation is static,
|
for m in members:
|
||||||
# then fail by way of creating the function, which will cause a
|
if m.identifier.name == name and m.isMethod() and not m.isStatic():
|
||||||
# naming conflict, per the spec.
|
return
|
||||||
if allowExistingOperations:
|
method = IDLMethod(self.location,
|
||||||
for m in members:
|
IDLUnresolvedIdentifier(self.location, name, allowDoubleUnderscore=chromeOnly),
|
||||||
if m.identifier.name == name and m.isMethod() and not m.isStatic():
|
returnType, args, maplikeOrSetlikeOrIterable=self)
|
||||||
return
|
# We need to be able to throw from declaration methods
|
||||||
|
method.addExtendedAttributes(
|
||||||
method = IDLMethod(self.location,
|
[IDLExtendedAttribute(self.location, ("Throws",))])
|
||||||
IDLUnresolvedIdentifier(self.location, name, allowDoubleUnderscore=chromeOnly),
|
if chromeOnly:
|
||||||
returnType, args, maplikeOrSetlike=self)
|
|
||||||
|
|
||||||
# We need to be able to throw from declaration methods
|
|
||||||
method.addExtendedAttributes(
|
method.addExtendedAttributes(
|
||||||
[IDLExtendedAttribute(self.location, ("Throws",))])
|
[IDLExtendedAttribute(self.location, ("ChromeOnly",))])
|
||||||
if chromeOnly:
|
if isPure:
|
||||||
method.addExtendedAttributes(
|
method.addExtendedAttributes(
|
||||||
[IDLExtendedAttribute(self.location, ("ChromeOnly",))])
|
[IDLExtendedAttribute(self.location, ("Pure",))])
|
||||||
if isPure:
|
# Following attributes are used for keys/values/entries. Can't mark
|
||||||
method.addExtendedAttributes(
|
# them pure, since they return a new object each time they are run.
|
||||||
[IDLExtendedAttribute(self.location, ("Pure",))])
|
if affectsNothing:
|
||||||
# Following attributes are used for keys/values/entries. Can't mark
|
method.addExtendedAttributes(
|
||||||
# them pure, since they return a new object each time they are run.
|
[IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
|
||||||
if affectsNothing:
|
IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
|
||||||
method.addExtendedAttributes(
|
if newObject:
|
||||||
[IDLExtendedAttribute(self.location, ("DependsOn", "Everything")),
|
method.addExtendedAttributes(
|
||||||
IDLExtendedAttribute(self.location, ("Affects", "Nothing"))])
|
[IDLExtendedAttribute(self.location, ("NewObject",))])
|
||||||
members.append(method)
|
members.append(method)
|
||||||
|
|
||||||
# Both maplike and setlike have a size attribute
|
|
||||||
members.append(IDLAttribute(self.location,
|
|
||||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
|
||||||
True,
|
|
||||||
maplikeOrSetlike=self))
|
|
||||||
self.reserved_ro_names = ["size"]
|
|
||||||
|
|
||||||
# object entries()
|
|
||||||
addMethod("entries", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
|
||||||
affectsNothing=True)
|
|
||||||
# object keys()
|
|
||||||
addMethod("keys", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
|
||||||
affectsNothing=True)
|
|
||||||
# object values()
|
|
||||||
addMethod("values", False, BuiltinTypes[IDLBuiltinType.Types.object],
|
|
||||||
affectsNothing=True)
|
|
||||||
|
|
||||||
# void forEach(callback(valueType, keyType), thisVal)
|
|
||||||
foreachArguments = [IDLArgument(self.location,
|
|
||||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
|
||||||
"callback"),
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.object]),
|
|
||||||
IDLArgument(self.location,
|
|
||||||
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
|
||||||
"thisArg"),
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.any],
|
|
||||||
optional=True)]
|
|
||||||
addMethod("forEach", False, BuiltinTypes[IDLBuiltinType.Types.void],
|
|
||||||
foreachArguments)
|
|
||||||
|
|
||||||
def getKeyArg():
|
|
||||||
return IDLArgument(self.location,
|
|
||||||
IDLUnresolvedIdentifier(self.location, "key"),
|
|
||||||
self.keyType)
|
|
||||||
|
|
||||||
# boolean has(keyType key)
|
|
||||||
addMethod("has", False, BuiltinTypes[IDLBuiltinType.Types.boolean],
|
|
||||||
[getKeyArg()], isPure=True)
|
|
||||||
|
|
||||||
if not self.readonly:
|
|
||||||
# void clear()
|
|
||||||
addMethod("clear", True, BuiltinTypes[IDLBuiltinType.Types.void],
|
|
||||||
[])
|
|
||||||
# boolean delete(keyType key)
|
|
||||||
addMethod("delete", True,
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()])
|
|
||||||
|
|
||||||
# Always generate underscored functions (e.g. __add, __clear) for js
|
|
||||||
# implemented interfaces as convenience functions.
|
|
||||||
if isJSImplemented:
|
|
||||||
# void clear()
|
|
||||||
addMethod("clear", True, BuiltinTypes[IDLBuiltinType.Types.void],
|
|
||||||
[], chromeOnly=True)
|
|
||||||
# boolean delete(keyType key)
|
|
||||||
addMethod("delete", True,
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()],
|
|
||||||
chromeOnly=True)
|
|
||||||
|
|
||||||
if self.isSetlike():
|
|
||||||
if not self.readonly:
|
|
||||||
# Add returns the set object it just added to.
|
|
||||||
# object add(keyType key)
|
|
||||||
|
|
||||||
addMethod("add", True,
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()])
|
|
||||||
if isJSImplemented:
|
|
||||||
addMethod("add", True,
|
|
||||||
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()],
|
|
||||||
chromeOnly=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
# If we get this far, we're a maplike declaration.
|
|
||||||
|
|
||||||
# valueType get(keyType key)
|
|
||||||
#
|
|
||||||
# Note that instead of the value type, we're using any here. The
|
|
||||||
# validity checks should happen as things are inserted into the map,
|
|
||||||
# and using any as the return type makes code generation much simpler.
|
|
||||||
#
|
|
||||||
# TODO: Bug 1155340 may change this to use specific type to provide
|
|
||||||
# more info to JIT.
|
|
||||||
addMethod("get", False, BuiltinTypes[IDLBuiltinType.Types.any],
|
|
||||||
[getKeyArg()], isPure=True)
|
|
||||||
|
|
||||||
def getValueArg():
|
|
||||||
return IDLArgument(self.location,
|
|
||||||
IDLUnresolvedIdentifier(self.location, "value"),
|
|
||||||
self.valueType)
|
|
||||||
|
|
||||||
if not self.readonly:
|
|
||||||
addMethod("set", True, BuiltinTypes[IDLBuiltinType.Types.object],
|
|
||||||
[getKeyArg(), getValueArg()])
|
|
||||||
if isJSImplemented:
|
|
||||||
addMethod("set", True, BuiltinTypes[IDLBuiltinType.Types.object],
|
|
||||||
[getKeyArg(), getValueArg()], chromeOnly=True)
|
|
||||||
|
|
||||||
def resolve(self, parentScope):
|
def resolve(self, parentScope):
|
||||||
self.keyType.resolveType(parentScope)
|
self.keyType.resolveType(parentScope)
|
||||||
self.valueType.resolveType(parentScope)
|
if self.valueType:
|
||||||
|
self.valueType.resolveType(parentScope)
|
||||||
|
|
||||||
def finish(self, scope):
|
def finish(self, scope):
|
||||||
IDLInterfaceMember.finish(self, scope)
|
IDLInterfaceMember.finish(self, scope)
|
||||||
@ -3701,7 +3611,7 @@ class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
|||||||
assert not isinstance(t, IDLTypedefType)
|
assert not isinstance(t, IDLTypedefType)
|
||||||
assert not isinstance(t.name, IDLUnresolvedIdentifier)
|
assert not isinstance(t.name, IDLUnresolvedIdentifier)
|
||||||
self.keyType = t
|
self.keyType = t
|
||||||
if not self.valueType.isComplete():
|
if self.valueType and not self.valueType.isComplete():
|
||||||
t = self.valueType.complete(scope)
|
t = self.valueType.complete(scope)
|
||||||
|
|
||||||
assert not isinstance(t, IDLUnresolvedType)
|
assert not isinstance(t, IDLUnresolvedType)
|
||||||
@ -3716,8 +3626,161 @@ class IDLMaplikeOrSetlike(IDLInterfaceMember):
|
|||||||
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
IDLInterfaceMember.handleExtendedAttribute(self, attr)
|
||||||
|
|
||||||
def _getDependentObjects(self):
|
def _getDependentObjects(self):
|
||||||
return set([self.keyType, self.valueType])
|
if self.valueType:
|
||||||
|
return set([self.keyType, self.valueType])
|
||||||
|
return set([self.keyType])
|
||||||
|
|
||||||
|
# Iterable adds ES6 iterator style functions and traits
|
||||||
|
# (keys/values/entries/@@iterator) to an interface.
|
||||||
|
class IDLIterable(IDLMaplikeOrSetlikeOrIterableBase):
|
||||||
|
|
||||||
|
def __init__(self, location, identifier, keyType, valueType=None, scope=None):
|
||||||
|
IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier,
|
||||||
|
"iterable", keyType, valueType,
|
||||||
|
IDLInterfaceMember.Tags.Iterable)
|
||||||
|
self.iteratorType = None
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "declared iterable with key '%s' and value '%s'" % (self.keyType, self.valueType)
|
||||||
|
|
||||||
|
def expand(self, members, isJSImplemented):
|
||||||
|
"""
|
||||||
|
In order to take advantage of all of the method machinery in Codegen,
|
||||||
|
we generate our functions as if they were part of the interface
|
||||||
|
specification during parsing.
|
||||||
|
"""
|
||||||
|
# object entries()
|
||||||
|
self.addMethod("entries", members, False, self.iteratorType,
|
||||||
|
affectsNothing=True, newObject=True)
|
||||||
|
# object keys()
|
||||||
|
self.addMethod("keys", members, False, self.iteratorType,
|
||||||
|
affectsNothing=True, newObject=True)
|
||||||
|
# object values()
|
||||||
|
self.addMethod("values", members, False, self.iteratorType,
|
||||||
|
affectsNothing=True, newObject=True)
|
||||||
|
|
||||||
|
# MaplikeOrSetlike adds ES6 map-or-set-like traits to an interface.
|
||||||
|
class IDLMaplikeOrSetlike(IDLMaplikeOrSetlikeOrIterableBase):
|
||||||
|
|
||||||
|
def __init__(self, location, identifier, maplikeOrSetlikeType,
|
||||||
|
readonly, keyType, valueType):
|
||||||
|
IDLMaplikeOrSetlikeOrIterableBase.__init__(self, location, identifier, maplikeOrSetlikeType,
|
||||||
|
keyType, valueType, IDLInterfaceMember.Tags.MaplikeOrSetlike)
|
||||||
|
self.readonly = readonly
|
||||||
|
self.slotIndex = None
|
||||||
|
|
||||||
|
# When generating JSAPI access code, we need to know the backing object
|
||||||
|
# type prefix to create the correct function. Generate here for reuse.
|
||||||
|
if self.isMaplike():
|
||||||
|
self.prefix = 'Map'
|
||||||
|
elif self.isSetlike():
|
||||||
|
self.prefix = 'Set'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "declared '%s' with key '%s'" % (self.maplikeOrSetlikeOrIterableType, self.keyType)
|
||||||
|
|
||||||
|
def expand(self, members, isJSImplemented):
|
||||||
|
"""
|
||||||
|
In order to take advantage of all of the method machinery in Codegen,
|
||||||
|
we generate our functions as if they were part of the interface
|
||||||
|
specification during parsing.
|
||||||
|
"""
|
||||||
|
# Both maplike and setlike have a size attribute
|
||||||
|
members.append(IDLAttribute(self.location,
|
||||||
|
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"), "size"),
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.unsigned_long],
|
||||||
|
True,
|
||||||
|
maplikeOrSetlike=self))
|
||||||
|
self.reserved_ro_names = ["size"]
|
||||||
|
|
||||||
|
# object entries()
|
||||||
|
self.addMethod("entries", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
affectsNothing=True)
|
||||||
|
# object keys()
|
||||||
|
self.addMethod("keys", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
affectsNothing=True)
|
||||||
|
# object values()
|
||||||
|
self.addMethod("values", members, False, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
affectsNothing=True)
|
||||||
|
|
||||||
|
# void forEach(callback(valueType, keyType), thisVal)
|
||||||
|
foreachArguments = [IDLArgument(self.location,
|
||||||
|
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
||||||
|
"callback"),
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.object]),
|
||||||
|
IDLArgument(self.location,
|
||||||
|
IDLUnresolvedIdentifier(BuiltinLocation("<auto-generated-identifier>"),
|
||||||
|
"thisArg"),
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.any],
|
||||||
|
optional=True)]
|
||||||
|
self.addMethod("forEach", members, False, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||||
|
foreachArguments)
|
||||||
|
|
||||||
|
def getKeyArg():
|
||||||
|
return IDLArgument(self.location,
|
||||||
|
IDLUnresolvedIdentifier(self.location, "key"),
|
||||||
|
self.keyType)
|
||||||
|
|
||||||
|
# boolean has(keyType key)
|
||||||
|
self.addMethod("has", members, False, BuiltinTypes[IDLBuiltinType.Types.boolean],
|
||||||
|
[getKeyArg()], isPure=True)
|
||||||
|
|
||||||
|
if not self.readonly:
|
||||||
|
# void clear()
|
||||||
|
self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||||
|
[])
|
||||||
|
# boolean delete(keyType key)
|
||||||
|
self.addMethod("delete", members, True,
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()])
|
||||||
|
|
||||||
|
# Always generate underscored functions (e.g. __add, __clear) for js
|
||||||
|
# implemented interfaces as convenience functions.
|
||||||
|
if isJSImplemented:
|
||||||
|
# void clear()
|
||||||
|
self.addMethod("clear", members, True, BuiltinTypes[IDLBuiltinType.Types.void],
|
||||||
|
[], chromeOnly=True)
|
||||||
|
# boolean delete(keyType key)
|
||||||
|
self.addMethod("delete", members, True,
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.boolean], [getKeyArg()],
|
||||||
|
chromeOnly=True)
|
||||||
|
|
||||||
|
if self.isSetlike():
|
||||||
|
if not self.readonly:
|
||||||
|
# Add returns the set object it just added to.
|
||||||
|
# object add(keyType key)
|
||||||
|
|
||||||
|
self.addMethod("add", members, True,
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()])
|
||||||
|
if isJSImplemented:
|
||||||
|
self.addMethod("add", members, True,
|
||||||
|
BuiltinTypes[IDLBuiltinType.Types.object], [getKeyArg()],
|
||||||
|
chromeOnly=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
# If we get this far, we're a maplike declaration.
|
||||||
|
|
||||||
|
# valueType get(keyType key)
|
||||||
|
#
|
||||||
|
# Note that instead of the value type, we're using any here. The
|
||||||
|
# validity checks should happen as things are inserted into the map,
|
||||||
|
# and using any as the return type makes code generation much simpler.
|
||||||
|
#
|
||||||
|
# TODO: Bug 1155340 may change this to use specific type to provide
|
||||||
|
# more info to JIT.
|
||||||
|
self.addMethod("get", members, False, BuiltinTypes[IDLBuiltinType.Types.any],
|
||||||
|
[getKeyArg()], isPure=True)
|
||||||
|
|
||||||
|
def getValueArg():
|
||||||
|
return IDLArgument(self.location,
|
||||||
|
IDLUnresolvedIdentifier(self.location, "value"),
|
||||||
|
self.valueType)
|
||||||
|
|
||||||
|
if not self.readonly:
|
||||||
|
self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
[getKeyArg(), getValueArg()])
|
||||||
|
if isJSImplemented:
|
||||||
|
self.addMethod("set", members, True, BuiltinTypes[IDLBuiltinType.Types.object],
|
||||||
|
[getKeyArg(), getValueArg()], chromeOnly=True)
|
||||||
|
|
||||||
class IDLConst(IDLInterfaceMember):
|
class IDLConst(IDLInterfaceMember):
|
||||||
def __init__(self, location, identifier, type, value):
|
def __init__(self, location, identifier, type, value):
|
||||||
@ -4319,7 +4382,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||||||
static=False, getter=False, setter=False, creator=False,
|
static=False, getter=False, setter=False, creator=False,
|
||||||
deleter=False, specialType=NamedOrIndexed.Neither,
|
deleter=False, specialType=NamedOrIndexed.Neither,
|
||||||
legacycaller=False, stringifier=False, jsonifier=False,
|
legacycaller=False, stringifier=False, jsonifier=False,
|
||||||
maplikeOrSetlike=None):
|
maplikeOrSetlikeOrIterable=None):
|
||||||
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
# REVIEW: specialType is NamedOrIndexed -- wow, this is messed up.
|
||||||
IDLInterfaceMember.__init__(self, location, identifier,
|
IDLInterfaceMember.__init__(self, location, identifier,
|
||||||
IDLInterfaceMember.Tags.Method)
|
IDLInterfaceMember.Tags.Method)
|
||||||
@ -4347,8 +4410,8 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||||||
self._stringifier = stringifier
|
self._stringifier = stringifier
|
||||||
assert isinstance(jsonifier, bool)
|
assert isinstance(jsonifier, bool)
|
||||||
self._jsonifier = jsonifier
|
self._jsonifier = jsonifier
|
||||||
assert maplikeOrSetlike is None or isinstance(maplikeOrSetlike, IDLMaplikeOrSetlike)
|
assert maplikeOrSetlikeOrIterable is None or isinstance(maplikeOrSetlikeOrIterable, IDLMaplikeOrSetlikeOrIterableBase)
|
||||||
self.maplikeOrSetlike = maplikeOrSetlike
|
self.maplikeOrSetlikeOrIterable = maplikeOrSetlikeOrIterable
|
||||||
self._specialType = specialType
|
self._specialType = specialType
|
||||||
self._unforgeable = False
|
self._unforgeable = False
|
||||||
self.dependsOn = "Everything"
|
self.dependsOn = "Everything"
|
||||||
@ -4430,12 +4493,12 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||||||
def isJsonifier(self):
|
def isJsonifier(self):
|
||||||
return self._jsonifier
|
return self._jsonifier
|
||||||
|
|
||||||
def isMaplikeOrSetlikeMethod(self):
|
def isMaplikeOrSetlikeOrIterableMethod(self):
|
||||||
"""
|
"""
|
||||||
True if this method was generated as part of a
|
True if this method was generated as part of a
|
||||||
maplike/setlike/etc interface (e.g. has/get methods)
|
maplike/setlike/etc interface (e.g. has/get methods)
|
||||||
"""
|
"""
|
||||||
return self.maplikeOrSetlike is not None
|
return self.maplikeOrSetlikeOrIterable is not None
|
||||||
|
|
||||||
def isSpecial(self):
|
def isSpecial(self):
|
||||||
return (self.isGetter() or
|
return (self.isGetter() or
|
||||||
@ -4458,7 +4521,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||||||
an non-identifier name, they actually DO have an identifier.
|
an non-identifier name, they actually DO have an identifier.
|
||||||
"""
|
"""
|
||||||
return (self.identifier.name[:2] == "__" and
|
return (self.identifier.name[:2] == "__" and
|
||||||
not self.isMaplikeOrSetlikeMethod())
|
not self.isMaplikeOrSetlikeOrIterableMethod())
|
||||||
|
|
||||||
def resolve(self, parentScope):
|
def resolve(self, parentScope):
|
||||||
assert isinstance(parentScope, IDLScope)
|
assert isinstance(parentScope, IDLScope)
|
||||||
@ -4966,7 +5029,8 @@ class Tokenizer(object):
|
|||||||
"SharedArrayBuffer": "SHAREDARRAYBUFFER",
|
"SharedArrayBuffer": "SHAREDARRAYBUFFER",
|
||||||
"or": "OR",
|
"or": "OR",
|
||||||
"maplike": "MAPLIKE",
|
"maplike": "MAPLIKE",
|
||||||
"setlike": "SETLIKE"
|
"setlike": "SETLIKE",
|
||||||
|
"iterable": "ITERABLE"
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens.extend(keywords.values())
|
tokens.extend(keywords.values())
|
||||||
@ -5120,8 +5184,9 @@ class Parser(Tokenizer):
|
|||||||
raise ex
|
raise ex
|
||||||
pass
|
pass
|
||||||
|
|
||||||
p[0] = IDLInterface(location, self.globalScope(), identifier, parent,
|
iface = IDLInterface(location, self.globalScope(), identifier, parent,
|
||||||
members, isKnownNonPartial=True)
|
members, isKnownNonPartial=True)
|
||||||
|
p[0] = iface
|
||||||
|
|
||||||
def p_InterfaceForwardDecl(self, p):
|
def p_InterfaceForwardDecl(self, p):
|
||||||
"""
|
"""
|
||||||
@ -5207,7 +5272,7 @@ class Parser(Tokenizer):
|
|||||||
def p_InterfaceMember(self, p):
|
def p_InterfaceMember(self, p):
|
||||||
"""
|
"""
|
||||||
InterfaceMember : Const
|
InterfaceMember : Const
|
||||||
| AttributeOrOperationOrMaplikeOrSetlike
|
| AttributeOrOperationOrMaplikeOrSetlikeOrIterable
|
||||||
"""
|
"""
|
||||||
p[0] = p[1]
|
p[0] = p[1]
|
||||||
|
|
||||||
@ -5422,15 +5487,30 @@ class Parser(Tokenizer):
|
|||||||
"""
|
"""
|
||||||
p[0] = False
|
p[0] = False
|
||||||
|
|
||||||
def p_AttributeOrOperationOrMaplikeOrSetlike(self, p):
|
def p_AttributeOrOperationOrMaplikeOrSetlikeOrIterable(self, p):
|
||||||
"""
|
"""
|
||||||
AttributeOrOperationOrMaplikeOrSetlike : Attribute
|
AttributeOrOperationOrMaplikeOrSetlikeOrIterable : Attribute
|
||||||
| Maplike
|
| Maplike
|
||||||
| Setlike
|
| Setlike
|
||||||
| Operation
|
| Iterable
|
||||||
|
| Operation
|
||||||
"""
|
"""
|
||||||
p[0] = p[1]
|
p[0] = p[1]
|
||||||
|
|
||||||
|
def p_Iterable(self, p):
|
||||||
|
"""
|
||||||
|
Iterable : ITERABLE LT Type GT SEMICOLON
|
||||||
|
| ITERABLE LT Type COMMA Type GT SEMICOLON
|
||||||
|
"""
|
||||||
|
location = self.getLocation(p, 2)
|
||||||
|
identifier = IDLUnresolvedIdentifier(location, "__iterable",
|
||||||
|
allowDoubleUnderscore=True)
|
||||||
|
keyType = p[3]
|
||||||
|
valueType = None
|
||||||
|
if (len(p) > 6):
|
||||||
|
valueType = p[5]
|
||||||
|
p[0] = IDLIterable(location, identifier, keyType, valueType, self.globalScope())
|
||||||
|
|
||||||
def p_Setlike(self, p):
|
def p_Setlike(self, p):
|
||||||
"""
|
"""
|
||||||
Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
|
Setlike : ReadOnly SETLIKE LT Type GT SEMICOLON
|
||||||
@ -5803,6 +5883,7 @@ class Parser(Tokenizer):
|
|||||||
| IMPLEMENTS
|
| IMPLEMENTS
|
||||||
| INHERIT
|
| INHERIT
|
||||||
| INTERFACE
|
| INTERFACE
|
||||||
|
| ITERABLE
|
||||||
| LEGACYCALLER
|
| LEGACYCALLER
|
||||||
| MAPLIKE
|
| MAPLIKE
|
||||||
| PARTIAL
|
| PARTIAL
|
||||||
@ -6481,7 +6562,46 @@ class Parser(Tokenizer):
|
|||||||
self._filename = None
|
self._filename = None
|
||||||
|
|
||||||
def finish(self):
|
def finish(self):
|
||||||
# First, finish all the IDLImplementsStatements. In particular, we
|
# If we have interfaces that are iterable, create their
|
||||||
|
# iterator interfaces and add them to the productions array.
|
||||||
|
interfaceStatements = [p for p in self._productions if
|
||||||
|
isinstance(p, IDLInterface)]
|
||||||
|
|
||||||
|
for iface in interfaceStatements:
|
||||||
|
iterable = None
|
||||||
|
# We haven't run finish() on the interface yet, so we don't know
|
||||||
|
# whether our interface is maplike/setlike/iterable or not. This
|
||||||
|
# means we have to loop through the members to see if we have an
|
||||||
|
# iterable member.
|
||||||
|
for m in iface.members:
|
||||||
|
if isinstance(m, IDLIterable):
|
||||||
|
iterable = m
|
||||||
|
break
|
||||||
|
if iterable:
|
||||||
|
itr_ident = IDLUnresolvedIdentifier(iface.location,
|
||||||
|
iface.identifier.name + "Iterator")
|
||||||
|
itr_iface = IDLInterface(iface.location, self.globalScope(),
|
||||||
|
itr_ident, None, [],
|
||||||
|
isKnownNonPartial=True)
|
||||||
|
itr_iface.addExtendedAttributes([IDLExtendedAttribute(iface.location,
|
||||||
|
("NoInterfaceObject", ))])
|
||||||
|
# Always append generated iterable interfaces and their
|
||||||
|
# matching implements statements after the interface they're a
|
||||||
|
# member of, otherwise nativeType generation won't work
|
||||||
|
# correctly.
|
||||||
|
itr_iface.iterableInterface = iface
|
||||||
|
self._productions.append(itr_iface)
|
||||||
|
iterable.iteratorType = IDLWrapperType(iface.location, itr_iface)
|
||||||
|
itrPlaceholder = IDLIdentifierPlaceholder(iface.location,
|
||||||
|
IDLUnresolvedIdentifier(iface.location,
|
||||||
|
"IterableIterator"))
|
||||||
|
implements = IDLImplementsStatement(iface.location,
|
||||||
|
IDLIdentifierPlaceholder(iface.location,
|
||||||
|
itr_ident),
|
||||||
|
itrPlaceholder)
|
||||||
|
self._productions.append(implements)
|
||||||
|
|
||||||
|
# Then, finish all the IDLImplementsStatements. In particular, we
|
||||||
# have to make sure we do those before we do the IDLInterfaces.
|
# have to make sure we do those before we do the IDLInterfaces.
|
||||||
# XXX khuey hates this bit and wants to nuke it from orbit.
|
# XXX khuey hates this bit and wants to nuke it from orbit.
|
||||||
implementsStatements = [p for p in self._productions if
|
implementsStatements = [p for p in self._productions if
|
||||||
|
@ -41,11 +41,11 @@ def WebIDLTest(parser, harness):
|
|||||||
prefix + " - Interface failed but not as a WebIDLError exception")
|
prefix + " - Interface failed but not as a WebIDLError exception")
|
||||||
|
|
||||||
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
|
iterableMembers = [(x, WebIDL.IDLMethod) for x in ["entries", "keys",
|
||||||
"values", "forEach"]]
|
"values"]]
|
||||||
iterableMembers.extend([("size", WebIDL.IDLAttribute)])
|
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has", "foreach"]] +
|
||||||
setROMembers = ([(x, WebIDL.IDLMethod) for x in ["has"]] +
|
|
||||||
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
|
[("__setlike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||||
iterableMembers)
|
iterableMembers)
|
||||||
|
setROMembers.extend([("size", WebIDL.IDLAttribute)])
|
||||||
setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
|
setRWMembers = ([(x, WebIDL.IDLMethod) for x in ["add",
|
||||||
"clear",
|
"clear",
|
||||||
"delete"]] +
|
"delete"]] +
|
||||||
@ -58,9 +58,10 @@ def WebIDLTest(parser, harness):
|
|||||||
"__clear",
|
"__clear",
|
||||||
"__delete"]] +
|
"__delete"]] +
|
||||||
setRWMembers)
|
setRWMembers)
|
||||||
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has"]] +
|
mapROMembers = ([(x, WebIDL.IDLMethod) for x in ["get", "has", "foreach"]] +
|
||||||
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
|
[("__maplike", WebIDL.IDLMaplikeOrSetlike)] +
|
||||||
iterableMembers)
|
iterableMembers)
|
||||||
|
mapROMembers.extend([("size", WebIDL.IDLAttribute)])
|
||||||
mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
|
mapRWMembers = ([(x, WebIDL.IDLMethod) for x in ["set",
|
||||||
"clear",
|
"clear",
|
||||||
"delete"]] + mapROMembers)
|
"delete"]] + mapROMembers)
|
||||||
@ -69,8 +70,8 @@ def WebIDLTest(parser, harness):
|
|||||||
"__delete"]] +
|
"__delete"]] +
|
||||||
mapRWMembers)
|
mapRWMembers)
|
||||||
|
|
||||||
disallowedMemberNames = ["keys", "entries", "values", "forEach", "has",
|
disallowedIterableNames = ["keys", "entries", "values"]
|
||||||
"size"]
|
disallowedMemberNames = ["forEach", "has", "size"] + disallowedIterableNames
|
||||||
mapDisallowedMemberNames = ["get"] + disallowedMemberNames
|
mapDisallowedMemberNames = ["get"] + disallowedMemberNames
|
||||||
disallowedNonMethodNames = ["clear", "delete"]
|
disallowedNonMethodNames = ["clear", "delete"]
|
||||||
mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
|
mapDisallowedNonMethodNames = ["set"] + disallowedNonMethodNames
|
||||||
@ -80,6 +81,27 @@ def WebIDLTest(parser, harness):
|
|||||||
# Simple Usage Tests
|
# Simple Usage Tests
|
||||||
#
|
#
|
||||||
|
|
||||||
|
shouldPass("Iterable (key only)",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
iterable<long>;
|
||||||
|
};
|
||||||
|
""", iterableMembers)
|
||||||
|
|
||||||
|
shouldPass("Iterable (key and value)",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
iterable<long, long>;
|
||||||
|
};
|
||||||
|
""", iterableMembers)
|
||||||
|
|
||||||
|
shouldPass("Maplike (readwrite)",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
maplike<long, long>;
|
||||||
|
};
|
||||||
|
""", mapRWMembers)
|
||||||
|
|
||||||
shouldPass("Maplike (readwrite)",
|
shouldPass("Maplike (readwrite)",
|
||||||
"""
|
"""
|
||||||
interface Foo1 {
|
interface Foo1 {
|
||||||
@ -157,6 +179,22 @@ def WebIDLTest(parser, harness):
|
|||||||
};
|
};
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
shouldFail("Two iterable/setlikes on same interface",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
iterable<long>;
|
||||||
|
maplike<long, long>;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
|
shouldFail("Two iterables on same interface",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
iterable<long>;
|
||||||
|
iterable<long, long>;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
shouldFail("Two maplike/setlikes in partials",
|
shouldFail("Two maplike/setlikes in partials",
|
||||||
"""
|
"""
|
||||||
interface Foo1 {
|
interface Foo1 {
|
||||||
@ -177,6 +215,16 @@ def WebIDLTest(parser, harness):
|
|||||||
};
|
};
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
shouldFail("Conflicting maplike/iterable across inheritance",
|
||||||
|
"""
|
||||||
|
interface Foo1 {
|
||||||
|
maplike<long, long>;
|
||||||
|
};
|
||||||
|
interface Foo2 : Foo1 {
|
||||||
|
iterable<long>;
|
||||||
|
};
|
||||||
|
""")
|
||||||
|
|
||||||
shouldFail("Conflicting maplike/setlikes across multistep inheritance",
|
shouldFail("Conflicting maplike/setlikes across multistep inheritance",
|
||||||
"""
|
"""
|
||||||
interface Foo1 {
|
interface Foo1 {
|
||||||
@ -283,6 +331,8 @@ def WebIDLTest(parser, harness):
|
|||||||
};
|
};
|
||||||
""" % (likeMember, conflictName))
|
""" % (likeMember, conflictName))
|
||||||
|
|
||||||
|
for member in disallowedIterableNames:
|
||||||
|
testConflictingMembers("iterable<long, long>", member, iterableMembers, False)
|
||||||
for member in mapDisallowedMemberNames:
|
for member in mapDisallowedMemberNames:
|
||||||
testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
|
testConflictingMembers("maplike<long, long>", member, mapRWMembers, False)
|
||||||
for member in disallowedMemberNames:
|
for member in disallowedMemberNames:
|
82
dom/bindings/test/TestInterfaceIterableDouble.cpp
Normal file
82
dom/bindings/test/TestInterfaceIterableDouble.cpp
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/* 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 "mozilla/dom/TestInterfaceIterableDouble.h"
|
||||||
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceIterableDouble, mParent)
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceIterableDouble)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceIterableDouble)
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableDouble)
|
||||||
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
TestInterfaceIterableDouble::TestInterfaceIterableDouble(nsPIDOMWindow* aParent)
|
||||||
|
: mParent(aParent)
|
||||||
|
{
|
||||||
|
mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("a"),
|
||||||
|
NS_LITERAL_STRING("b")));
|
||||||
|
mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("c"),
|
||||||
|
NS_LITERAL_STRING("d")));
|
||||||
|
mValues.AppendElement(std::pair<nsString, nsString>(NS_LITERAL_STRING("e"),
|
||||||
|
NS_LITERAL_STRING("f")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
already_AddRefed<TestInterfaceIterableDouble>
|
||||||
|
TestInterfaceIterableDouble::Constructor(const GlobalObject& aGlobal,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
|
if (!window) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<TestInterfaceIterableDouble> r = new TestInterfaceIterableDouble(window);
|
||||||
|
return r.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
TestInterfaceIterableDouble::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||||
|
{
|
||||||
|
return TestInterfaceIterableDoubleBinding::Wrap(aCx, this, aGivenProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPIDOMWindow*
|
||||||
|
TestInterfaceIterableDouble::GetParentObject() const
|
||||||
|
{
|
||||||
|
return mParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
TestInterfaceIterableDouble::GetIterableLength()
|
||||||
|
{
|
||||||
|
return mValues.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAString&
|
||||||
|
TestInterfaceIterableDouble::GetKeyAtIndex(uint32_t aIndex)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aIndex < mValues.Length());
|
||||||
|
return mValues.ElementAt(aIndex).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAString&
|
||||||
|
TestInterfaceIterableDouble::GetValueAtIndex(uint32_t aIndex)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aIndex < mValues.Length());
|
||||||
|
return mValues.ElementAt(aIndex).second;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
51
dom/bindings/test/TestInterfaceIterableDouble.h
Normal file
51
dom/bindings/test/TestInterfaceIterableDouble.h
Normal file
@ -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 mozilla_dom_TestInterfaceIterableDouble_h
|
||||||
|
#define mozilla_dom_TestInterfaceIterableDouble_h
|
||||||
|
|
||||||
|
#include "nsWrapperCache.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
|
||||||
|
class nsPIDOMWindow;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
class ErrorResult;
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class GlobalObject;
|
||||||
|
|
||||||
|
// Implementation of test binding for webidl iterable interfaces, using
|
||||||
|
// primitives for value type
|
||||||
|
class TestInterfaceIterableDouble final : public nsISupports,
|
||||||
|
public nsWrapperCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceIterableDouble)
|
||||||
|
|
||||||
|
explicit TestInterfaceIterableDouble(nsPIDOMWindow* aParent);
|
||||||
|
nsPIDOMWindow* GetParentObject() const;
|
||||||
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
|
JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
static already_AddRefed<TestInterfaceIterableDouble>
|
||||||
|
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||||
|
|
||||||
|
size_t GetIterableLength();
|
||||||
|
nsAString& GetKeyAtIndex(uint32_t aIndex);
|
||||||
|
nsAString& GetValueAtIndex(uint32_t aIndex);
|
||||||
|
private:
|
||||||
|
virtual ~TestInterfaceIterableDouble() {}
|
||||||
|
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||||
|
nsTArray<std::pair<nsString, nsString>> mValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_TestInterfaceIterableDouble_h
|
78
dom/bindings/test/TestInterfaceIterableSingle.cpp
Normal file
78
dom/bindings/test/TestInterfaceIterableSingle.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* 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 "mozilla/dom/TestInterfaceIterableSingle.h"
|
||||||
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
|
#include "nsPIDOMWindow.h"
|
||||||
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TestInterfaceIterableSingle, mParent)
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(TestInterfaceIterableSingle)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(TestInterfaceIterableSingle)
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TestInterfaceIterableSingle)
|
||||||
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
TestInterfaceIterableSingle::TestInterfaceIterableSingle(nsPIDOMWindow* aParent)
|
||||||
|
: mParent(aParent)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 3; ++i) {
|
||||||
|
mValues.AppendElement(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//static
|
||||||
|
already_AddRefed<TestInterfaceIterableSingle>
|
||||||
|
TestInterfaceIterableSingle::Constructor(const GlobalObject& aGlobal,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
|
if (!window) {
|
||||||
|
aRv.Throw(NS_ERROR_FAILURE);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsRefPtr<TestInterfaceIterableSingle> r = new TestInterfaceIterableSingle(window);
|
||||||
|
return r.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
TestInterfaceIterableSingle::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||||
|
{
|
||||||
|
return TestInterfaceIterableSingleBinding::Wrap(aCx, this, aGivenProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsPIDOMWindow*
|
||||||
|
TestInterfaceIterableSingle::GetParentObject() const
|
||||||
|
{
|
||||||
|
return mParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
TestInterfaceIterableSingle::GetIterableLength() const
|
||||||
|
{
|
||||||
|
return mValues.Length();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
TestInterfaceIterableSingle::GetKeyAtIndex(uint32_t index) const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(index < mValues.Length());
|
||||||
|
return mValues.ElementAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
TestInterfaceIterableSingle::GetValueAtIndex(uint32_t index) const
|
||||||
|
{
|
||||||
|
return GetKeyAtIndex(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
51
dom/bindings/test/TestInterfaceIterableSingle.h
Normal file
51
dom/bindings/test/TestInterfaceIterableSingle.h
Normal file
@ -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 mozilla_dom_TestInterfaceIterableSingle_h
|
||||||
|
#define mozilla_dom_TestInterfaceIterableSingle_h
|
||||||
|
|
||||||
|
#include "nsWrapperCache.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
|
||||||
|
class nsPIDOMWindow;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
class ErrorResult;
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class GlobalObject;
|
||||||
|
|
||||||
|
// Implementation of test binding for webidl iterable interfaces, using
|
||||||
|
// primitives for value type
|
||||||
|
class TestInterfaceIterableSingle final : public nsISupports,
|
||||||
|
public nsWrapperCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TestInterfaceIterableSingle)
|
||||||
|
|
||||||
|
explicit TestInterfaceIterableSingle(nsPIDOMWindow* aParent);
|
||||||
|
nsPIDOMWindow* GetParentObject() const;
|
||||||
|
virtual JSObject* WrapObject(JSContext* aCx,
|
||||||
|
JS::Handle<JSObject*> aGivenProto) override;
|
||||||
|
static already_AddRefed<TestInterfaceIterableSingle>
|
||||||
|
Constructor(const GlobalObject& aGlobal, ErrorResult& rv);
|
||||||
|
|
||||||
|
size_t GetIterableLength() const;
|
||||||
|
uint32_t GetKeyAtIndex(uint32_t aIndex) const;
|
||||||
|
uint32_t GetValueAtIndex(uint32_t aIndex) const;
|
||||||
|
private:
|
||||||
|
virtual ~TestInterfaceIterableSingle() {}
|
||||||
|
nsCOMPtr<nsPIDOMWindow> mParent;
|
||||||
|
nsTArray<uint32_t> mValues;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_TestInterfaceIterableSingle_h
|
@ -3,7 +3,7 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/dom/TestInterfaceMaplike.h"
|
#include "mozilla/dom/TestInterfaceMaplike.h"
|
||||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#include "mozilla/dom/TestInterfaceMaplikeObject.h"
|
#include "mozilla/dom/TestInterfaceMaplikeObject.h"
|
||||||
#include "mozilla/dom/TestInterfaceMaplike.h"
|
#include "mozilla/dom/TestInterfaceMaplike.h"
|
||||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/dom/TestInterfaceSetlike.h"
|
#include "mozilla/dom/TestInterfaceSetlike.h"
|
||||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/dom/TestInterfaceSetlikeNode.h"
|
#include "mozilla/dom/TestInterfaceSetlikeNode.h"
|
||||||
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeBinding.h"
|
#include "mozilla/dom/TestInterfaceJSMaplikeSetlikeIterableBinding.h"
|
||||||
#include "nsPIDOMWindow.h"
|
#include "nsPIDOMWindow.h"
|
||||||
#include "mozilla/dom/BindingUtils.h"
|
#include "mozilla/dom/BindingUtils.h"
|
||||||
|
|
||||||
|
@ -68,3 +68,5 @@ skip-if = debug == false
|
|||||||
skip-if = debug == false
|
skip-if = debug == false
|
||||||
[test_jsimplemented_eventhandler.html]
|
[test_jsimplemented_eventhandler.html]
|
||||||
skip-if = debug == false
|
skip-if = debug == false
|
||||||
|
[test_iterable.html]
|
||||||
|
skip-if = debug == false
|
@ -57,7 +57,6 @@
|
|||||||
var m;
|
var m;
|
||||||
var testSet;
|
var testSet;
|
||||||
var testIndex;
|
var testIndex;
|
||||||
var iterable;
|
|
||||||
// Simple map creation and functionality test
|
// Simple map creation and functionality test
|
||||||
info("SimpleMap: Testing simple map creation and functionality");
|
info("SimpleMap: Testing simple map creation and functionality");
|
||||||
m = new TestInterfaceMaplike();
|
m = new TestInterfaceMaplike();
|
||||||
|
112
dom/bindings/test/test_iterable.html
Normal file
112
dom/bindings/test/test_iterable.html
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<!-- Any copyright is dedicated to the Public Domain.
|
||||||
|
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Test Iterable Interface</title>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script class="testbody" type="application/javascript">
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]}, function() {
|
||||||
|
|
||||||
|
base_properties = [["entries", "function", 0],
|
||||||
|
["keys", "function", 0],
|
||||||
|
["values", "function", 0]]
|
||||||
|
var testExistence = function testExistence(prefix, obj, properties) {
|
||||||
|
for (var [name, type, args] of properties) {
|
||||||
|
// Properties are somewhere up the proto chain, hasOwnProperty won't work
|
||||||
|
isnot(obj[name], undefined,
|
||||||
|
`${prefix} object has property ${name}`);
|
||||||
|
|
||||||
|
is(typeof obj[name], type,
|
||||||
|
`${prefix} object property ${name} is a ${type}`);
|
||||||
|
// Check function length
|
||||||
|
if (type == "function") {
|
||||||
|
is(obj[name].length, args,
|
||||||
|
`${prefix} object property ${name} is length ${args}`);
|
||||||
|
is(obj[name].name, name,
|
||||||
|
`${prefix} object method name is ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find where property is on proto chain, check for enumerablility there.
|
||||||
|
var owner = obj;
|
||||||
|
while (owner) {
|
||||||
|
var propDesc = Object.getOwnPropertyDescriptor(owner, name);
|
||||||
|
if (propDesc) {
|
||||||
|
ok(propDesc.enumerable,
|
||||||
|
`${prefix} object property ${name} is enumerable`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
owner = Object.getPrototypeOf(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var itr;
|
||||||
|
// Simple single type iterable creation and functionality test
|
||||||
|
info("IterableSingle: Testing simple iterable creation and functionality");
|
||||||
|
itr = new TestInterfaceIterableSingle();
|
||||||
|
testExistence("IterableSingle: ", itr, base_properties);
|
||||||
|
var key_itr = itr.keys();
|
||||||
|
var value_itr = itr.values();
|
||||||
|
var entries_itr = itr.entries();
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
var key = key_itr.next();
|
||||||
|
var value = value_itr.next();
|
||||||
|
var entry = entries_itr.next();
|
||||||
|
is(key.value, i, "IterableSingle: Key iterator value should be " + i);
|
||||||
|
is(value.value, key.value, "IterableSingle: Value iterator value should be " + key.value);
|
||||||
|
is(entry.value[0], i, "IterableSingle: Entry iterator value 0 should be " + i);
|
||||||
|
is(entry.value[1], i, "IterableSingle: Entry iterator value 1 should be " + i);
|
||||||
|
}
|
||||||
|
var key = key_itr.next();
|
||||||
|
var value = value_itr.next();
|
||||||
|
var entry = entries_itr.next();
|
||||||
|
is(key.value, undefined, "IterableSingle: Key iterator value should be undefined");
|
||||||
|
is(key.done, true, "IterableSingle: Key iterator done should be true");
|
||||||
|
is(value.value, undefined, "IterableSingle: Value iterator value should be undefined");
|
||||||
|
is(value.done, true, "IterableSingle: Value iterator done should be true");
|
||||||
|
is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
|
||||||
|
is(entry.done, true, "IterableSingle: Entry iterator done should be true");
|
||||||
|
is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
|
||||||
|
"[object TestInterfaceIterableSingleIteratorPrototype]",
|
||||||
|
"iterator prototype should have the right brand");
|
||||||
|
|
||||||
|
// Simple dual type iterable creation and functionality test
|
||||||
|
info("IterableDouble: Testing simple iterable creation and functionality");
|
||||||
|
itr = new TestInterfaceIterableDouble();
|
||||||
|
testExistence("IterableDouble: ", itr, base_properties);
|
||||||
|
var elements = [["a", "b"], ["c", "d"], ["e", "f"]]
|
||||||
|
var key_itr = itr.keys();
|
||||||
|
var value_itr = itr.values();
|
||||||
|
var entries_itr = itr.entries();
|
||||||
|
for (var i = 0; i < 3; ++i) {
|
||||||
|
var key = key_itr.next();
|
||||||
|
var value = value_itr.next();
|
||||||
|
var entry = entries_itr.next();
|
||||||
|
is(key.value, elements[i][0], "IterableDouble: Key iterator value should be " + elements[i][0]);
|
||||||
|
is(value.value, elements[i][1], "IterableDouble: Value iterator value should be " + elements[i][1]);
|
||||||
|
is(entry.value[0], elements[i][0], "IterableDouble: Entry iterator value 0 should be " + elements[i][0]);
|
||||||
|
is(entry.value[1], elements[i][1], "IterableDouble: Entry iterator value 1 should be " + elements[i][1]);
|
||||||
|
}
|
||||||
|
var key = key_itr.next();
|
||||||
|
var value = value_itr.next();
|
||||||
|
var entry = entries_itr.next()
|
||||||
|
is(key.value, undefined, "IterableDouble: Key iterator value should be undefined");
|
||||||
|
is(key.done, true, "IterableDouble: Key iterator done should be true");
|
||||||
|
is(value.value, undefined, "IterableDouble: Value iterator value should be undefined");
|
||||||
|
is(value.done, true, "IterableDouble: Value iterator done should be true");
|
||||||
|
is(entry.value, undefined, "IterableDouble: Entry iterator value should be undefined");
|
||||||
|
is(entry.done, true, "IterableDouble: Entry iterator done should be true");
|
||||||
|
is(Object.prototype.toString.call(Object.getPrototypeOf(key_itr)),
|
||||||
|
"[object TestInterfaceIterableDoubleIteratorPrototype]",
|
||||||
|
"iterator prototype should have the right brand");
|
||||||
|
|
||||||
|
SimpleTest.finish();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
dom/cache/Context.cpp
vendored
10
dom/cache/Context.cpp
vendored
@ -1001,7 +1001,15 @@ Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
|
|||||||
MOZ_ASSERT(!mDirectoryLock);
|
MOZ_ASSERT(!mDirectoryLock);
|
||||||
mDirectoryLock = aDirectoryLock;
|
mDirectoryLock = aDirectoryLock;
|
||||||
|
|
||||||
if (mState == STATE_CONTEXT_CANCELED || NS_FAILED(aRv)) {
|
// If we opening the context failed, but we were not explicitly canceled,
|
||||||
|
// still treat the entire context as canceled. We don't want to allow
|
||||||
|
// new actions to be dispatched. We also cannot leave the context in
|
||||||
|
// the INIT state after failing to open.
|
||||||
|
if (NS_FAILED(aRv)) {
|
||||||
|
mState = STATE_CONTEXT_CANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState == STATE_CONTEXT_CANCELED) {
|
||||||
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
|
||||||
mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
|
mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
|
||||||
}
|
}
|
||||||
|
2
dom/cache/TypeUtils.cpp
vendored
2
dom/cache/TypeUtils.cpp
vendored
@ -202,7 +202,7 @@ TypeUtils::ToCacheResponseWithoutBody(CacheResponse& aOut,
|
|||||||
{
|
{
|
||||||
aOut.type() = aIn.Type();
|
aOut.type() = aIn.Type();
|
||||||
|
|
||||||
aIn.GetUrl(aOut.url());
|
aIn.GetUnfilteredUrl(aOut.url());
|
||||||
|
|
||||||
if (aOut.url() != EmptyCString()) {
|
if (aOut.url() != EmptyCString()) {
|
||||||
// Pass all Response URL schemes through... The spec only requires we take
|
// Pass all Response URL schemes through... The spec only requires we take
|
||||||
|
@ -400,7 +400,9 @@ HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
|
|||||||
// Add Observer for webgl canvas.
|
// Add Observer for webgl canvas.
|
||||||
if (aContextType == CanvasContextType::WebGL1 ||
|
if (aContextType == CanvasContextType::WebGL1 ||
|
||||||
aContextType == CanvasContextType::WebGL2) {
|
aContextType == CanvasContextType::WebGL2) {
|
||||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
if (!mContextObserver) {
|
||||||
|
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret->SetCanvasElement(this);
|
ret->SetCanvasElement(this);
|
||||||
@ -779,7 +781,9 @@ HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
|
|||||||
sz.height,
|
sz.height,
|
||||||
GetCompositorBackendType(),
|
GetCompositorBackendType(),
|
||||||
renderer);
|
renderer);
|
||||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
if (!mContextObserver) {
|
||||||
|
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
}
|
}
|
||||||
|
@ -151,6 +151,7 @@ public:
|
|||||||
return nsRefPtr<ImportLoader>(mImportLoader).forget();
|
return nsRefPtr<ImportLoader>(mImportLoader).forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual CORSMode GetCORSMode() const override;
|
||||||
protected:
|
protected:
|
||||||
virtual ~HTMLLinkElement();
|
virtual ~HTMLLinkElement();
|
||||||
|
|
||||||
@ -161,7 +162,6 @@ protected:
|
|||||||
nsAString& aMedia,
|
nsAString& aMedia,
|
||||||
bool* aIsScoped,
|
bool* aIsScoped,
|
||||||
bool* aIsAlternate) override;
|
bool* aIsAlternate) override;
|
||||||
virtual CORSMode GetCORSMode() const override;
|
|
||||||
protected:
|
protected:
|
||||||
// nsGenericHTMLElement
|
// nsGenericHTMLElement
|
||||||
virtual void GetItemValueText(DOMString& text) override;
|
virtual void GetItemValueText(DOMString& text) override;
|
||||||
|
@ -5,9 +5,7 @@ support-files =
|
|||||||
|
|
||||||
[test_Document-createElement-namespace.html.json]
|
[test_Document-createElement-namespace.html.json]
|
||||||
[test_Document-createElementNS.html.json]
|
[test_Document-createElementNS.html.json]
|
||||||
[test_Document-getElementsByTagName.html.json]
|
|
||||||
[test_Node-properties.html.json]
|
[test_Node-properties.html.json]
|
||||||
[test_attributes.html.json]
|
[test_attributes.html.json]
|
||||||
[test_case.html.json]
|
[test_case.html.json]
|
||||||
[test_getElementsByClassName-10.xml.json]
|
|
||||||
[test_getElementsByClassName-11.xml.json]
|
[test_getElementsByClassName-11.xml.json]
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"Document.getElementsByTagName 1": true,
|
|
||||||
"Document.getElementsByTagName 2": true
|
|
||||||
}
|
|
@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"getElementsByTagName abc": true,
|
"getElementsByTagName abc": true,
|
||||||
"getElementsByTagName Abc": true,
|
"getElementsByTagName Abc": true,
|
||||||
"getElementsByTagName ABC": true,
|
"getElementsByTagName ABC": true
|
||||||
"getElementsByTagName \u00e4": true,
|
|
||||||
"getElementsByTagName \u00c4": true
|
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"document.getElementsByClassName(): compound": true
|
|
||||||
}
|
|
@ -36,7 +36,7 @@ public:
|
|||||||
nsresult Flush();
|
nsresult Flush();
|
||||||
|
|
||||||
// Shutdown decoder and rejects the init promise.
|
// Shutdown decoder and rejects the init promise.
|
||||||
nsresult Shutdown();
|
virtual nsresult Shutdown();
|
||||||
|
|
||||||
// True if sample is queued.
|
// True if sample is queued.
|
||||||
bool HasQueuedSample();
|
bool HasQueuedSample();
|
||||||
|
@ -59,16 +59,22 @@ GonkVideoDecoderManager::GonkVideoDecoderManager(
|
|||||||
nsIntSize frameSize(mVideoWidth, mVideoHeight);
|
nsIntSize frameSize(mVideoWidth, mVideoHeight);
|
||||||
mPicture = pictureRect;
|
mPicture = pictureRect;
|
||||||
mInitialFrame = frameSize;
|
mInitialFrame = frameSize;
|
||||||
mVideoListener = new VideoResourceListener(this);
|
mVideoListener = new VideoResourceListener();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GonkVideoDecoderManager::~GonkVideoDecoderManager()
|
GonkVideoDecoderManager::~GonkVideoDecoderManager()
|
||||||
{
|
{
|
||||||
mVideoListener->NotifyManagerRelease();
|
|
||||||
MOZ_COUNT_DTOR(GonkVideoDecoderManager);
|
MOZ_COUNT_DTOR(GonkVideoDecoderManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GonkVideoDecoderManager::Shutdown()
|
||||||
|
{
|
||||||
|
mVideoCodecRequest.DisconnectIfExists();
|
||||||
|
return GonkDecoderManager::Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
nsRefPtr<MediaDataDecoder::InitPromise>
|
nsRefPtr<MediaDataDecoder::InitPromise>
|
||||||
GonkVideoDecoderManager::Init()
|
GonkVideoDecoderManager::Init()
|
||||||
{
|
{
|
||||||
@ -107,6 +113,16 @@ GonkVideoDecoderManager::Init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsRefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
|
nsRefPtr<InitPromise> p = mInitPromise.Ensure(__func__);
|
||||||
|
android::sp<GonkVideoDecoderManager> self = this;
|
||||||
|
mVideoCodecRequest.Begin(mVideoListener->Init()
|
||||||
|
->Then(mReaderTaskQueue, __func__,
|
||||||
|
[self] (bool) -> void {
|
||||||
|
self->mVideoCodecRequest.Complete();
|
||||||
|
self->codecReserved();
|
||||||
|
}, [self] (bool) -> void {
|
||||||
|
self->mVideoCodecRequest.Complete();
|
||||||
|
self->codecCanceled();
|
||||||
|
}));
|
||||||
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false, mVideoListener);
|
mDecoder = MediaCodecProxy::CreateByType(mDecodeLooper, mMimeType.get(), false, mVideoListener);
|
||||||
mDecoder->AsyncAskMediaCodec();
|
mDecoder->AsyncAskMediaCodec();
|
||||||
|
|
||||||
@ -399,6 +415,9 @@ void GonkVideoDecoderManager::ReleaseVideoBuffer() {
|
|||||||
void
|
void
|
||||||
GonkVideoDecoderManager::codecReserved()
|
GonkVideoDecoderManager::codecReserved()
|
||||||
{
|
{
|
||||||
|
if (mInitPromise.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
GVDM_LOG("codecReserved");
|
GVDM_LOG("codecReserved");
|
||||||
sp<AMessage> format = new AMessage;
|
sp<AMessage> format = new AMessage;
|
||||||
sp<Surface> surface;
|
sp<Surface> surface;
|
||||||
@ -427,7 +446,7 @@ GonkVideoDecoderManager::codecReserved()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInitPromise.ResolveIfExists(TrackType::kVideoTrack, __func__);
|
mInitPromise.Resolve(TrackType::kVideoTrack, __func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -456,66 +475,24 @@ GonkVideoDecoderManager::onMessageReceived(const sp<AMessage> &aMessage)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GonkVideoDecoderManager::VideoResourceListener::VideoResourceListener(GonkVideoDecoderManager *aManager)
|
GonkVideoDecoderManager::VideoResourceListener::VideoResourceListener()
|
||||||
: mManager(aManager)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
GonkVideoDecoderManager::VideoResourceListener::~VideoResourceListener()
|
GonkVideoDecoderManager::VideoResourceListener::~VideoResourceListener()
|
||||||
{
|
{
|
||||||
mManager = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GonkVideoDecoderManager::VideoResourceListener::codecReserved()
|
GonkVideoDecoderManager::VideoResourceListener::codecReserved()
|
||||||
{
|
{
|
||||||
// This class holds VideoResourceListener reference to prevent it's destroyed.
|
mVideoCodecPromise.Resolve(true, __func__);
|
||||||
class CodecListenerHolder : public nsRunnable {
|
|
||||||
public:
|
|
||||||
CodecListenerHolder(VideoResourceListener* aListener)
|
|
||||||
: mVideoListener(aListener) {}
|
|
||||||
|
|
||||||
NS_IMETHOD Run()
|
|
||||||
{
|
|
||||||
mVideoListener->NotifyCodecReserved();
|
|
||||||
mVideoListener = nullptr;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
android::sp<VideoResourceListener> mVideoListener;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (mManager) {
|
|
||||||
nsRefPtr<CodecListenerHolder> runner = new CodecListenerHolder(this);
|
|
||||||
mManager->mReaderTaskQueue->Dispatch(runner.forget());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GonkVideoDecoderManager::VideoResourceListener::codecCanceled()
|
GonkVideoDecoderManager::VideoResourceListener::codecCanceled()
|
||||||
{
|
{
|
||||||
if (mManager) {
|
mVideoCodecPromise.Reject(true, __func__);
|
||||||
MOZ_ASSERT(mManager->mReaderTaskQueue->IsCurrentThreadIn());
|
|
||||||
nsCOMPtr<nsIRunnable> r =
|
|
||||||
NS_NewNonOwningRunnableMethod(mManager, &GonkVideoDecoderManager::codecCanceled);
|
|
||||||
mManager->mReaderTaskQueue->Dispatch(r.forget());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::VideoResourceListener::NotifyManagerRelease()
|
|
||||||
{
|
|
||||||
MOZ_ASSERT_IF(mManager, mManager->mReaderTaskQueue->IsCurrentThreadIn());
|
|
||||||
mManager = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GonkVideoDecoderManager::VideoResourceListener::NotifyCodecReserved()
|
|
||||||
{
|
|
||||||
if (mManager) {
|
|
||||||
MOZ_ASSERT(mManager->mReaderTaskQueue->IsCurrentThreadIn());
|
|
||||||
mManager->codecReserved();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *
|
uint8_t *
|
||||||
|
@ -36,6 +36,8 @@ typedef android::MediaCodecProxy MediaCodecProxy;
|
|||||||
typedef mozilla::layers::TextureClient TextureClient;
|
typedef mozilla::layers::TextureClient TextureClient;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
typedef MozPromise<bool /* aIgnored */, bool /* aIgnored */, /* IsExclusive = */ true> MediaResourcePromise;
|
||||||
|
|
||||||
GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
|
GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
|
||||||
const VideoInfo& aConfig);
|
const VideoInfo& aConfig);
|
||||||
|
|
||||||
@ -46,6 +48,8 @@ public:
|
|||||||
nsresult Output(int64_t aStreamOffset,
|
nsresult Output(int64_t aStreamOffset,
|
||||||
nsRefPtr<MediaData>& aOutput) override;
|
nsRefPtr<MediaData>& aOutput) override;
|
||||||
|
|
||||||
|
nsresult Shutdown() override;
|
||||||
|
|
||||||
static void RecycleCallback(TextureClient* aClient, void* aClosure);
|
static void RecycleCallback(TextureClient* aClient, void* aClosure);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -67,24 +71,25 @@ private:
|
|||||||
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VideoResourceListener(GonkVideoDecoderManager *aManager);
|
VideoResourceListener();
|
||||||
~VideoResourceListener();
|
~VideoResourceListener();
|
||||||
|
|
||||||
|
nsRefPtr<MediaResourcePromise> Init()
|
||||||
|
{
|
||||||
|
nsRefPtr<MediaResourcePromise> p = mVideoCodecPromise.Ensure(__func__);
|
||||||
|
return p.forget();
|
||||||
|
}
|
||||||
|
|
||||||
void codecReserved() override;
|
void codecReserved() override;
|
||||||
void codecCanceled() override;
|
void codecCanceled() override;
|
||||||
|
|
||||||
void NotifyManagerRelease();
|
|
||||||
void NotifyCodecReserved();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Forbidden
|
// Forbidden
|
||||||
VideoResourceListener() = delete;
|
|
||||||
VideoResourceListener(const VideoResourceListener &rhs) = delete;
|
VideoResourceListener(const VideoResourceListener &rhs) = delete;
|
||||||
const VideoResourceListener &operator=(const VideoResourceListener &rhs) = delete;
|
const VideoResourceListener &operator=(const VideoResourceListener &rhs) = delete;
|
||||||
|
|
||||||
GonkVideoDecoderManager *mManager;
|
MozPromiseHolder<MediaResourcePromise> mVideoCodecPromise;
|
||||||
};
|
};
|
||||||
friend class VideoResourceListener;
|
|
||||||
|
|
||||||
bool SetVideoFormat();
|
bool SetVideoFormat();
|
||||||
|
|
||||||
@ -113,6 +118,7 @@ private:
|
|||||||
|
|
||||||
MediaInfo mInfo;
|
MediaInfo mInfo;
|
||||||
android::sp<VideoResourceListener> mVideoListener;
|
android::sp<VideoResourceListener> mVideoListener;
|
||||||
|
MozPromiseRequestHolder<MediaResourcePromise> mVideoCodecRequest;
|
||||||
FrameInfo mFrameInfo;
|
FrameInfo mFrameInfo;
|
||||||
|
|
||||||
// color converter
|
// color converter
|
||||||
|
@ -1847,6 +1847,10 @@ nsPluginHost::SiteHasData(nsIPluginTag* plugin, const nsACString& domain,
|
|||||||
nsPluginHost::SpecialType
|
nsPluginHost::SpecialType
|
||||||
nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
|
nsPluginHost::GetSpecialType(const nsACString & aMIMEType)
|
||||||
{
|
{
|
||||||
|
if (aMIMEType.LowerCaseEqualsASCII("application/x-test")) {
|
||||||
|
return eSpecialType_Test;
|
||||||
|
}
|
||||||
|
|
||||||
if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
|
if (aMIMEType.LowerCaseEqualsASCII("application/x-shockwave-flash") ||
|
||||||
aMIMEType.LowerCaseEqualsASCII("application/futuresplash")) {
|
aMIMEType.LowerCaseEqualsASCII("application/futuresplash")) {
|
||||||
return eSpecialType_Flash;
|
return eSpecialType_Flash;
|
||||||
@ -2446,6 +2450,7 @@ nsPluginHost::FindPluginsInContent(bool aCreatePluginList, bool* aPluginsChanged
|
|||||||
nsTArray<nsCString>(tag.extensions()),
|
nsTArray<nsCString>(tag.extensions()),
|
||||||
tag.isJavaPlugin(),
|
tag.isJavaPlugin(),
|
||||||
tag.isFlashPlugin(),
|
tag.isFlashPlugin(),
|
||||||
|
tag.supportsAsyncInit(),
|
||||||
tag.lastModifiedTime(),
|
tag.lastModifiedTime(),
|
||||||
tag.isFromExtension());
|
tag.isFromExtension());
|
||||||
AddPluginTag(pluginTag);
|
AddPluginTag(pluginTag);
|
||||||
@ -2682,6 +2687,7 @@ nsPluginHost::FindPluginsForContent(uint32_t aPluginEpoch,
|
|||||||
tag->Extensions(),
|
tag->Extensions(),
|
||||||
tag->mIsJavaPlugin,
|
tag->mIsJavaPlugin,
|
||||||
tag->mIsFlashPlugin,
|
tag->mIsFlashPlugin,
|
||||||
|
tag->mSupportsAsyncInit,
|
||||||
tag->FileName(),
|
tag->FileName(),
|
||||||
tag->Version(),
|
tag->Version(),
|
||||||
tag->mLastModifiedTime,
|
tag->mLastModifiedTime,
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user